@dolby-optiview/ads-sdk-core 1.0.0-beta.1
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/OptiViewAds.d.ts +190 -0
- package/dist/OptiViewAds.js +1 -0
- package/dist/config/keys.d.ts +23 -0
- package/dist/config/keys.js +1 -0
- package/dist/diagnostics/DiagnosticBuffer.d.ts +33 -0
- package/dist/diagnostics/DiagnosticBuffer.js +1 -0
- package/dist/diagnostics/DiagnosticEvent.d.ts +71 -0
- package/dist/diagnostics/DiagnosticEvent.js +1 -0
- package/dist/diagnostics/debugLog.d.ts +8 -0
- package/dist/diagnostics/debugLog.js +1 -0
- package/dist/diagnostics/errorCodes.d.ts +45 -0
- package/dist/diagnostics/errorCodes.js +1 -0
- package/dist/diagnostics/index.d.ts +6 -0
- package/dist/diagnostics/index.js +1 -0
- package/dist/diagnostics/playbackErrorContext.d.ts +22 -0
- package/dist/diagnostics/playbackErrorContext.js +1 -0
- package/dist/generated/diagnosticCodes.d.ts +487 -0
- package/dist/generated/diagnosticCodes.js +1 -0
- package/dist/generated/version.d.ts +5 -0
- package/dist/generated/version.js +1 -0
- package/dist/id3/buildId3.d.ts +25 -0
- package/dist/id3/buildId3.js +1 -0
- package/dist/id3/index.d.ts +3 -0
- package/dist/id3/index.js +1 -0
- package/dist/id3/parseId3Geob.d.ts +28 -0
- package/dist/id3/parseId3Geob.js +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +1 -0
- package/dist/interfaces/PlayerAdapter.d.ts +163 -0
- package/dist/interfaces/PlayerAdapter.js +1 -0
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.js +1 -0
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +1 -0
- package/dist/services/AdBreakSequencer.d.ts +146 -0
- package/dist/services/AdBreakSequencer.js +1 -0
- package/dist/services/AdPlayerController.d.ts +495 -0
- package/dist/services/AdPlayerController.js +1 -0
- package/dist/services/BreakScheduler.d.ts +215 -0
- package/dist/services/BreakScheduler.js +1 -0
- package/dist/services/BreakTransitionTimer.d.ts +48 -0
- package/dist/services/BreakTransitionTimer.js +1 -0
- package/dist/services/CmcdAdReporter.d.ts +81 -0
- package/dist/services/CmcdAdReporter.js +1 -0
- package/dist/services/GamPodAdTracker.d.ts +65 -0
- package/dist/services/GamPodAdTracker.js +1 -0
- package/dist/services/GamPodUrlBuilder.d.ts +30 -0
- package/dist/services/GamPodUrlBuilder.js +1 -0
- package/dist/services/GamStreamManager.d.ts +67 -0
- package/dist/services/GamStreamManager.js +1 -0
- package/dist/services/LensReporter.d.ts +30 -0
- package/dist/services/LensReporter.js +1 -0
- package/dist/services/ManifestService.d.ts +91 -0
- package/dist/services/ManifestService.js +1 -0
- package/dist/services/MetadataCueTracker.d.ts +44 -0
- package/dist/services/MetadataCueTracker.js +1 -0
- package/dist/services/PauseAdController.d.ts +108 -0
- package/dist/services/PauseAdController.js +1 -0
- package/dist/services/RewindRearm.d.ts +109 -0
- package/dist/services/RewindRearm.js +1 -0
- package/dist/services/SsaiController.d.ts +59 -0
- package/dist/services/SsaiController.js +1 -0
- package/dist/services/StitcherUrlBuilder.d.ts +34 -0
- package/dist/services/StitcherUrlBuilder.js +1 -0
- package/dist/services/VastAdManager.d.ts +89 -0
- package/dist/services/VastAdManager.js +1 -0
- package/dist/services/adPlayer/AdPlayerEvents.d.ts +39 -0
- package/dist/services/adPlayer/AdPlayerEvents.js +1 -0
- package/dist/services/adPlayer/AdPlayerState.d.ts +98 -0
- package/dist/services/adPlayer/AdPlayerState.js +1 -0
- package/dist/services/adPlayer/AdPreloader.d.ts +54 -0
- package/dist/services/adPlayer/AdPreloader.js +1 -0
- package/dist/services/adPlayer/AdStage.d.ts +57 -0
- package/dist/services/adPlayer/AdStage.js +1 -0
- package/dist/services/adPlayer/AssetParameterResolver.d.ts +44 -0
- package/dist/services/adPlayer/AssetParameterResolver.js +1 -0
- package/dist/services/adPlayer/AssetPlayback.d.ts +71 -0
- package/dist/services/adPlayer/AssetPlayback.js +1 -0
- package/dist/services/adPlayer/BreakSequenceDriver.d.ts +46 -0
- package/dist/services/adPlayer/BreakSequenceDriver.js +1 -0
- package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +44 -0
- package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -0
- package/dist/services/adPlayer/ContinueContentPlayout.d.ts +26 -0
- package/dist/services/adPlayer/ContinueContentPlayout.js +1 -0
- package/dist/services/adPlayer/GamPodBridge.d.ts +66 -0
- package/dist/services/adPlayer/GamPodBridge.js +1 -0
- package/dist/services/adPlayer/PauseAdPresenter.d.ts +60 -0
- package/dist/services/adPlayer/PauseAdPresenter.js +1 -0
- package/dist/services/adPlayer/PresentationModeTracker.d.ts +66 -0
- package/dist/services/adPlayer/PresentationModeTracker.js +1 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +54 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -0
- package/dist/services/adPlayer/VariantSelector.d.ts +38 -0
- package/dist/services/adPlayer/VariantSelector.js +1 -0
- package/dist/services/adPlayer/connectPauseAd.d.ts +10 -0
- package/dist/services/adPlayer/connectPauseAd.js +1 -0
- package/dist/services/adPlayer/liveEdge.d.ts +6 -0
- package/dist/services/adPlayer/liveEdge.js +1 -0
- package/dist/services/assetParameterMacroTable.d.ts +9 -0
- package/dist/services/assetParameterMacroTable.js +1 -0
- package/dist/services/assetParameterMacros.d.ts +69 -0
- package/dist/services/assetParameterMacros.js +1 -0
- package/dist/services/breakTiming.d.ts +60 -0
- package/dist/services/breakTiming.js +1 -0
- package/dist/services/channelSwitch.d.ts +32 -0
- package/dist/services/channelSwitch.js +1 -0
- package/dist/services/detectDeviceType.d.ts +15 -0
- package/dist/services/detectDeviceType.js +1 -0
- package/dist/services/detectInsertionMode.d.ts +38 -0
- package/dist/services/detectInsertionMode.js +1 -0
- package/dist/services/detectPreloadMode.d.ts +13 -0
- package/dist/services/detectPreloadMode.js +1 -0
- package/dist/services/ed25519.d.ts +19 -0
- package/dist/services/ed25519.js +1 -0
- package/dist/services/fetchWithTimeout.d.ts +35 -0
- package/dist/services/fetchWithTimeout.js +1 -0
- package/dist/services/lensBackend.d.ts +16 -0
- package/dist/services/lensBackend.js +1 -0
- package/dist/services/lensBackendDevelopment.d.ts +1 -0
- package/dist/services/lensBackendDevelopment.js +1 -0
- package/dist/services/lensBackendProduction.d.ts +9 -0
- package/dist/services/lensBackendProduction.js +1 -0
- package/dist/services/manifestSignature.d.ts +43 -0
- package/dist/services/manifestSignature.js +1 -0
- package/dist/services/manifestTrustedKeys.d.ts +9 -0
- package/dist/services/manifestTrustedKeys.js +1 -0
- package/dist/services/parseBreakManifest.d.ts +15 -0
- package/dist/services/parseBreakManifest.js +1 -0
- package/dist/services/parseVastCompanion.d.ts +31 -0
- package/dist/services/parseVastCompanion.js +1 -0
- package/dist/services/parseVastLinear.d.ts +36 -0
- package/dist/services/parseVastLinear.js +1 -0
- package/dist/services/pictureInPicture.d.ts +28 -0
- package/dist/services/pictureInPicture.js +1 -0
- package/dist/services/resolveAssetParameters.d.ts +6 -0
- package/dist/services/resolveAssetParameters.js +1 -0
- package/dist/services/resolveDeliveryMode.d.ts +54 -0
- package/dist/services/resolveDeliveryMode.js +1 -0
- package/dist/services/resolveGamVendorConfiguration.d.ts +55 -0
- package/dist/services/resolveGamVendorConfiguration.js +1 -0
- package/dist/services/resolvePauseResumePoint.d.ts +32 -0
- package/dist/services/resolvePauseResumePoint.js +1 -0
- package/dist/services/resolvePollingDecision.d.ts +24 -0
- package/dist/services/resolvePollingDecision.js +1 -0
- package/dist/services/resolveResumePoint.d.ts +38 -0
- package/dist/services/resolveResumePoint.js +1 -0
- package/dist/services/resolveTargetedUri.d.ts +22 -0
- package/dist/services/resolveTargetedUri.js +1 -0
- package/dist/services/resolveVastAdmission.d.ts +34 -0
- package/dist/services/resolveVastAdmission.js +1 -0
- package/dist/services/selectVariant.d.ts +47 -0
- package/dist/services/selectVariant.js +1 -0
- package/dist/services/urlOrigin.d.ts +23 -0
- package/dist/services/urlOrigin.js +1 -0
- package/dist/session/AdBreakStatusTracker.d.ts +49 -0
- package/dist/session/AdBreakStatusTracker.js +1 -0
- package/dist/session/BreakLifecycle.d.ts +62 -0
- package/dist/session/BreakLifecycle.js +1 -0
- package/dist/session/SessionStarter.d.ts +98 -0
- package/dist/session/SessionStarter.js +1 -0
- package/dist/session/SessionState.d.ts +37 -0
- package/dist/session/SessionState.js +1 -0
- package/dist/types/BreakManifest.d.ts +335 -0
- package/dist/types/BreakManifest.js +1 -0
- package/dist/types/Events.d.ts +245 -0
- package/dist/types/Events.js +1 -0
- package/dist/types/OptiViewAdsConfig.d.ts +425 -0
- package/dist/types/OptiViewAdsConfig.js +1 -0
- package/dist/types/VendorAdError.d.ts +12 -0
- package/dist/types/VendorAdError.js +1 -0
- package/dist/vendor/lens-sdk/THIRD-PARTY-NOTICES.txt +266 -0
- package/dist/vendor/lens-sdk/compat.d.ts +9 -0
- package/dist/vendor/lens-sdk/encoding-cmcd.d.ts +9 -0
- package/dist/vendor/lens-sdk/encoding-json.d.ts +9 -0
- package/dist/vendor/lens-sdk/encoding.d.ts +42 -0
- package/dist/vendor/lens-sdk/environment.d.ts +35 -0
- package/dist/vendor/lens-sdk/index.d.ts +3 -0
- package/dist/vendor/lens-sdk/index.js +1 -0
- package/dist/vendor/lens-sdk/legacy.d.ts +12 -0
- package/dist/vendor/lens-sdk/lens-core-Q5lbQGMC.js +1 -0
- package/dist/vendor/lens-sdk/lens-global.d.ts +23 -0
- package/dist/vendor/lens-sdk/lens.d.ts +21 -0
- package/dist/vendor/lens-sdk/transport.d.ts +46 -0
- package/dist/vendor/lens-sdk/types.d.ts +218 -0
- package/dist/vendor/lens-sdk/user-id.d.ts +5 -0
- package/dist/vendor/lens-sdk/validation.d.ts +30 -0
- package/package.json +57 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DeviceType } from '../types/BreakManifest';
|
|
2
|
+
/**
|
|
3
|
+
* Heuristic: classify a User-Agent string into a {@link DeviceType} for
|
|
4
|
+
* per-URI asset targeting (see {@link resolveTargetedUri}).
|
|
5
|
+
*
|
|
6
|
+
* This is web-platform glue, not portable brain behaviour — Kotlin/Swift
|
|
7
|
+
* determine their device class natively (Android TV / tvOS APIs) rather than
|
|
8
|
+
* by parsing a User-Agent string, so this heuristic has no cross-language
|
|
9
|
+
* conformance fixture (mirrors `isSharedElementUserAgent` /
|
|
10
|
+
* `isSingleDecoderUserAgent`, which are likewise UA-only web glue).
|
|
11
|
+
*
|
|
12
|
+
* Checked in order TV → tablet → mobile → desktop (default), since a tablet
|
|
13
|
+
* or TV UA can otherwise coincidentally match a broader mobile pattern.
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectDeviceType(ua?: string): DeviceType;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DeviceType as e}from"../types/BreakManifest";export function detectDeviceType(t=("undefined"!=typeof navigator?navigator.userAgent:"")){return t?/SMART-TV|SmartTV|Tizen|Web0S|WebOS|GoogleTV|AppleTV|HbbTV|NetCast|VIDAA|BRAVIA/i.test(t)?e.TV:/iPad/i.test(t)||/Android/i.test(t)&&!/Mobile/i.test(t)?e.TABLET:/iPhone|iPod|Mobi|Android.*Mobile/i.test(t)?e.MOBILE:e.DESKTOP:e.DESKTOP}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AdInsertionMode } from '../types/OptiViewAdsConfig';
|
|
2
|
+
/**
|
|
3
|
+
* Concrete insertion strategy (`auto` is always resolved before the controller).
|
|
4
|
+
* - `overlay`: separate `<video>` layered over content; all formats.
|
|
5
|
+
* - `shared-element`: ad plays through the content `<video>` so iOS native fullscreen survives;
|
|
6
|
+
* single fullscreen ads only, `lshape_content` skipped.
|
|
7
|
+
* - `adaptive`: iPhone/iPod on iOS 17.1+ (Managed Media Source): `overlay` while inline,
|
|
8
|
+
* `shared-element` only in OS-native fullscreen where DOM overlays cannot render.
|
|
9
|
+
*/
|
|
10
|
+
export type ResolvedInsertionMode = 'overlay' | 'shared-element' | 'adaptive';
|
|
11
|
+
/** Media Source capabilities; on iPhone/iPod the insertion choice hinges on them. */
|
|
12
|
+
export interface MediaSourceCapabilities {
|
|
13
|
+
/** Native (unmanaged) Media Source Extensions — `window.MediaSource`. */
|
|
14
|
+
hasNativeMse: boolean;
|
|
15
|
+
/** Managed Media Source — `window.ManagedMediaSource` (iOS 17.1+). */
|
|
16
|
+
hasManagedMediaSource: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* iPhone / iPod: fullscreen is the OS native player, where DOM overlays cannot render. iPad is
|
|
20
|
+
* excluded (element fullscreen + MSE work there; iPadOS 13+ also reports a "Macintosh" UA).
|
|
21
|
+
*/
|
|
22
|
+
export declare function isSharedElementUserAgent(ua: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Handheld/tablet web device where a split (two-box) layout cannot be read, so the manifest
|
|
25
|
+
* spec's platform-capability fallback degrades advanced formats to a single fullscreen ad.
|
|
26
|
+
* Android TV and other big-screen UAs are excluded. Web counterpart of the Kotlin fallback.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSingleSurfaceWebDevice(ua: string, maxTouchPoints?: number): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* iPhone Safari had no Media Source, forcing native HLS through one `<video>` (the precondition
|
|
31
|
+
* for shared-element). iOS 17.1+ `ManagedMediaSource` lets HLS.js drive the overlay compositor.
|
|
32
|
+
*/
|
|
33
|
+
export declare function detectMediaSourceCapabilities(): MediaSourceCapabilities;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a configured AdInsertionMode. `auto` on iPhone/iPod: native MSE → `overlay`; Managed
|
|
36
|
+
* Media Source → `adaptive`; neither (iOS < 17.1) → `shared-element`. Other UAs → `overlay`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveInsertionMode(mode: AdInsertionMode, ua?: string, caps?: MediaSourceCapabilities): ResolvedInsertionMode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isSharedElementUserAgent(e){return!!e&&/iPhone|iPod/i.test(e)}export function isSingleSurfaceWebDevice(e,t=0){return!!e&&(!/\b(TV|SMART-TV|CrKey|AFT[A-Z]?)\b/i.test(e)&&(!(!/iPad|iPhone|iPod/i.test(e)&&!/Android/i.test(e))||/Macintosh/i.test(e)&&t>1))}export function detectMediaSourceCapabilities(){return{hasNativeMse:"undefined"!=typeof MediaSource,hasManagedMediaSource:void 0!==globalThis.ManagedMediaSource}}export function resolveInsertionMode(e,t=("undefined"!=typeof navigator?navigator.userAgent:""),i=detectMediaSourceCapabilities()){return"overlay"===e?"overlay":"shared-element"===e?isSharedElementUserAgent(t)?"shared-element":(console.warn('[OptiViewAds] adInsertion "shared-element" is only supported on iPhone/iPod (native HLS); falling back to "overlay" insertion on this platform.'),"overlay"):"adaptive"===e?"adaptive":isSharedElementUserAgent(t)?i.hasNativeMse?"overlay":i.hasManagedMediaSource?"adaptive":"shared-element":"overlay"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AdPreloadMode } from '../types/OptiViewAdsConfig';
|
|
2
|
+
/** Concrete preload strategy (`auto` is always resolved before the controller). */
|
|
3
|
+
export type ResolvedPreloadMode = 'parallel' | 'single-decoder' | 'none';
|
|
4
|
+
/**
|
|
5
|
+
* Single-decoder Smart TV that cannot buffer a second MediaSource beside content. EVERY Samsung
|
|
6
|
+
* (Tizen), LG (webOS) and VIZIO (SmartCast) TV, regardless of version: a 2024 Tizen 8 panel
|
|
7
|
+
* deadlocked on a second attached MediaSource (black screen, no events), so version is not a
|
|
8
|
+
* reliable signal. Cheap to be broad: `single-decoder` still warms the HTTP cache and prefetches
|
|
9
|
+
* detached. Unknown UAs (desktop, mobile) are treated as parallel-capable.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSingleDecoderUserAgent(ua: string): boolean;
|
|
12
|
+
/** Resolve a configured AdPreloadMode; `adapterSupportsParallel === false` forces `single-decoder` in `auto`. */
|
|
13
|
+
export declare function resolvePreloadMode(mode: AdPreloadMode, ua?: string, adapterSupportsParallel?: boolean): ResolvedPreloadMode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isSingleDecoderUserAgent(e){return!!e&&(!!/Tizen/i.test(e)||(!!/Web[0O]S/i.test(e)||(!!/VIZIO/i.test(e)||!!/SmartCast/i.test(e))))}export function resolvePreloadMode(e,n=("undefined"!=typeof navigator?navigator.userAgent:""),r){return"parallel"===e?"parallel":"single-decoder"===e?"single-decoder":"none"===e?"none":!1===r||isSingleDecoderUserAgent(n)?"single-decoder":"parallel"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 signature verification (RFC 8032), ported from the public-domain
|
|
3
|
+
* TweetNaCl reference implementation (`crypto_sign_open`, verify path only).
|
|
4
|
+
*
|
|
5
|
+
* Self-contained on purpose: `crypto.subtle` is async (this verifier must stay
|
|
6
|
+
* synchronous), missing entirely in insecure contexts, and its Ed25519 support
|
|
7
|
+
* never reached the legacy TV browsers this core still runs on, while
|
|
8
|
+
* `node:crypto` does not exist in the browser. Vendoring the verify path keeps
|
|
9
|
+
* `@dolby-optiview/ads-sdk-core` at zero runtime dependencies, and — because the Kotlin
|
|
10
|
+
* (`Ed25519.kt`) and Swift (`Ed25519.swift`) cores port the same TweetNaCl
|
|
11
|
+
* lineage — keeps all three cores in agreement on edge cases. SHA-512
|
|
12
|
+
* (TweetNaCl `crypto_hash`) is included for the same reason.
|
|
13
|
+
*
|
|
14
|
+
* Field elements are 16 radix-2^16 limbs in `Float64Array`s with carries via
|
|
15
|
+
* exact double arithmetic — the same representation TweetNaCl's own JS build
|
|
16
|
+
* uses, so every intermediate stays well inside the 53-bit integer range.
|
|
17
|
+
*/
|
|
18
|
+
/** Verify a detached Ed25519 `signature` over `message` with a raw 32-byte `publicKey`. */
|
|
19
|
+
export declare function ed25519Verify(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=16,n=Float64Array.from([61785,9906,39828,60374,45398,33411,5274,224,53552,61171,33010,6542,64743,22239,55772,9222]),r=Float64Array.from([54554,36645,11616,51542,42930,38181,51040,26924,56412,64982,57905,49316,21502,52590,14035,8553]),o=Float64Array.from([26200,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214,26214]),e=Float64Array.from([41136,18958,6951,50414,58488,44335,6150,12099,55207,15867,153,11085,57099,20417,9344,11139]),f=Float64Array.from([30883,4953,19914,30187,55467,16705,2637,112,59544,30585,16505,36039,65139,11119,27886,20995]),l=Float64Array.from([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]);function c(){return new Float64Array(t)}function a(){const t=c();return t[0]=1,t}function u(n){let r=1;for(let o=0;o<t;o++){const t=n[o]+r+65535;r=Math.floor(t/65536),n[o]=t-65536*r}n[0]+=r-1+37*(r-1)}function s(n,r,o){const e=~(o-1);for(let o=0;o<t;o++){const t=e&(n[o]^r[o]);n[o]^=t,r[o]^=t}}function i(n,r){const o=c(),e=new Float64Array(r);u(e),u(e),u(e);for(let t=0;t<2;t++){o[0]=e[0]-65517;for(let t=1;t<15;t++)o[t]=e[t]-65535-(o[t-1]>>16&1),o[t-1]&=65535;o[15]=e[15]-32767-(o[14]>>16&1);const t=o[15]>>16&1;o[14]&=65535,s(e,o,1-t)}for(let r=0;r<t;r++)n[2*r]=255&e[r],n[2*r+1]=e[r]>>8&255}function y(t,n){const r=new Uint8Array(32),o=new Uint8Array(32);i(r,t),i(o,n);let e=0;for(let t=0;t<32;t++)e|=r[t]^o[t];return 0!==e}function A(t){const n=new Uint8Array(32);return i(n,t),1&n[0]}function F(n,r,o){for(let e=0;e<t;e++)n[e]=r[e]+o[e]}function w(n,r,o){for(let e=0;e<t;e++)n[e]=r[e]-o[e]}function h(n,r,o){const e=new Float64Array(31);for(let n=0;n<t;n++)for(let f=0;f<t;f++)e[n+f]+=r[n]*o[f];for(let t=0;t<15;t++)e[t]+=38*e[t+16];for(let r=0;r<t;r++)n[r]=e[r];u(n),u(n)}function g(t,n){h(t,n,n)}function m(t,r){const o=c(),e=c(),f=c(),l=c(),a=c(),u=c(),s=c(),i=c(),y=c();w(o,t[1],t[0]),w(y,r[1],r[0]),h(o,o,y),F(e,t[0],t[1]),F(y,r[0],r[1]),h(e,e,y),h(f,t[3],r[3]),h(f,f,n),h(l,t[2],r[2]),F(l,l,l),w(a,e,o),w(u,l,f),F(s,l,f),F(i,e,o),h(t[0],a,u),h(t[1],i,s),h(t[2],s,u),h(t[3],a,i)}function U(t,n,r){for(let o=0;o<4;o++)s(t[o],n[o],r)}function M(t,n){const r=c(),o=c(),e=c();!function(t,n){const r=new Float64Array(n);for(let t=253;t>=0;t--)g(r,r),2!==t&&4!==t&&h(r,r,n);t.set(r)}(e,n[2]),h(r,n[0],e),h(o,n[1],e),i(t,o),t[31]^=A(r)<<7}function b(t,n,r){t[0]=c(),t[1]=a(),t[2]=a(),t[3]=c();for(let o=255;o>=0;o--){const e=r[o>>3]>>(7&o)&1;U(t,n,e),m(n,t),m(t,t),U(t,n,e)}}function d(n,r){const o=c(),l=c(),u=c(),s=c(),i=c(),m=c(),U=c();return n[2]=a(),n[0]=c(),n[1]=c(),n[3]=c(),function(n,r){for(let o=0;o<t;o++)n[o]=r[2*o]+(r[2*o+1]<<8);n[15]&=32767}(n[1],r),g(u,n[1]),h(s,u,f),w(u,u,n[2]),F(s,n[2],s),g(i,s),g(m,i),h(U,m,i),h(o,U,u),h(o,o,s),function(t,n){const r=new Float64Array(n);for(let t=250;t>=0;t--)g(r,r),1!==t&&h(r,r,n);t.set(r)}(o,o),h(o,o,u),h(o,o,s),h(o,o,s),h(n[0],o,s),g(l,n[0]),h(l,l,s),y(l,u)&&h(n[0],n[0],e),g(l,n[0]),h(l,l,s),!y(l,u)&&(A(n[0])===r[31]>>7&&w(n[0],c(),n[0]),h(n[3],n[0],n[1]),!0)}function p(t){const n=new Float64Array(64);for(let r=0;r<64;r++)n[r]=t[r];for(let n=0;n<64;n++)t[n]=0;!function(t,n){let r;for(let t=63;t>=32;t--){r=0;let o=t-32;for(;o<t-12;)n[o]+=r-16*n[t]*l[o-(t-32)],r=Math.floor((n[o]+128)/256),n[o]-=256*r,o++;n[o]+=r,n[t]=0}r=0;for(let t=0;t<32;t++)n[t]+=r-(n[31]>>4)*l[t],r=n[t]>>8,n[t]&=255;for(let t=0;t<32;t++)n[t]-=r*l[t];for(let r=0;r<32;r++)n[r+1]+=n[r]>>8,t[r]=255&n[r]}(t,n)}export function ed25519Verify(t,n,e){if(64!==n.length||32!==e.length)return!1;const f=[c(),c(),c(),c()];if(!d(f,e))return!1;const l=new Uint8Array(32+e.length+t.length);l.set(n.subarray(0,32)),l.set(e,32),l.set(t,64);const u=function(t){const n=Float64Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),r=Float64Array.from([4089235720,2227873595,4271175723,1595750129,2917565137,725511199,4215389547,327033209]),o=function(t){const n=t.length%128,r=new Uint8Array(t.length-n+(n<112?128:256));r.set(t),r[t.length]=128;const o=8*t.length,e=Math.floor(o/4294967296),f=o>>>0;for(let t=0;t<4;t++)r[r.length-8+t]=e>>>24-8*t&255,r[r.length-4+t]=f>>>24-8*t&255;return r}(t),e=new Float64Array(80),f=new Float64Array(80);for(let t=0;t<o.length;t+=128){for(let n=0;n<16;n++){const r=t+8*n;e[n]=(o[r]<<24|o[r+1]<<16|o[r+2]<<8|o[r+3])>>>0,f[n]=(o[r+4]<<24|o[r+5]<<16|o[r+6]<<8|o[r+7])>>>0}for(let t=16;t<80;t++){const[n,r]=j(e[t-15],f[t-15],1),[o,l]=j(e[t-15],f[t-15],8),[c,a]=k(e[t-15],f[t-15],7),[u,s]=j(e[t-2],f[t-2],19),[i,y]=j(e[t-2],f[t-2],61),[A,F]=k(e[t-2],f[t-2],6);[e[t],f[t]]=q(e[t-16],f[t-16],(n^o^c)>>>0,(r^l^a)>>>0,e[t-7],f[t-7],(u^i^A)>>>0,(s^y^F)>>>0)}const l=new Float64Array(n),c=new Float64Array(r);for(let t=0;t<80;t++){const[n,r]=j(l[4],c[4],14),[o,a]=j(l[4],c[4],18),[u,s]=j(l[4],c[4],41),[i,y]=q(l[7],c[7],(n^o^u)>>>0,(r^a^s)>>>0,(l[4]&l[5]^~l[4]&l[6])>>>0,(c[4]&c[5]^~c[4]&c[6])>>>0,x[t],V[t],e[t],f[t]),[A,F]=j(l[0],c[0],28),[w,h]=j(l[0],c[0],34),[g,m]=j(l[0],c[0],39),[U,M]=q((A^w^g)>>>0,(F^h^m)>>>0,(l[0]&l[1]^l[0]&l[2]^l[1]&l[2])>>>0,(c[0]&c[1]^c[0]&c[2]^c[1]&c[2])>>>0);l[7]=l[6],c[7]=c[6],l[6]=l[5],c[6]=c[5],l[5]=l[4],c[5]=c[4],[l[4],c[4]]=q(l[3],c[3],i,y),l[3]=l[2],c[3]=c[2],l[2]=l[1],c[2]=c[1],l[1]=l[0],c[1]=c[0],[l[0],c[0]]=q(i,y,U,M)}for(let t=0;t<8;t++)[n[t],r[t]]=q(n[t],r[t],l[t],c[t])}const l=new Uint8Array(64);for(let t=0;t<8;t++)for(let o=0;o<4;o++)l[8*t+o]=n[t]>>>24-8*o&255,l[8*t+4+o]=r[t]>>>24-8*o&255;return l}(l);p(u);const s=[c(),c(),c(),c()];b(s,f,u);const i=[c(),c(),c(),c()];!function(t,n){const e=[new Float64Array(r),new Float64Array(o),a(),c()];h(e[3],r,o),b(t,e,n)}(i,n.subarray(32,64)),m(s,i);const y=new Uint8Array(32);M(y,s);let A=0;for(let t=0;t<32;t++)A|=y[t]^n[t];return 0===A}const x=Float64Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298,3391569614,3515267271,3940187606,4118630271,116418474,174292421,289380356,460393269,685471733,852142971,1017036298,1126000580,1288033470,1501505948,1607167915,1816402316]),V=Float64Array.from([3609767458,602891725,3964484399,2173295548,4081628472,3053834265,2937671579,3664609560,2734883394,1164996542,1323610764,3590304994,4068182383,991336113,633803317,3479774868,2666613458,944711139,2341262773,2007800933,1495990901,1856431235,3175218132,2198950837,3999719339,766784016,2566594879,3203337956,1034457026,2466948901,3758326383,168717936,1188179964,1546045734,1522805485,2643833823,2343527390,1014477480,1206759142,344077627,1290863460,3158454273,3505952657,106217008,3606008344,1432725776,1467031594,851169720,3100823752,1363258195,3750685593,3785050280,3318307427,3812723403,2003034995,3602036899,1575990012,1125592928,2716904306,442776044,593698344,3733110249,2999351573,3815920427,3928383900,566280711,3454069534,4000239992,1914138554,2731055270,3203993006,320620315,587496836,1086792851,365543100,2618297676,3409855158,4234509866,987167468,1246189591]);function j(t,n,r){if(r<32)return[(t>>>r|n<<32-r)>>>0,(n>>>r|t<<32-r)>>>0];const o=r-32;return[(n>>>o|t<<32-o)>>>0,(t>>>o|n<<32-o)>>>0]}function k(t,n,r){return[t>>>r,(n>>>r|t<<32-r)>>>0]}function q(...t){let n=0,r=0;for(let o=0;o<t.length;o+=2)n+=t[o],r+=t[o+1];return n+=Math.floor(r/4294967296),[n>>>0,r>>>0]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded `fetch` — the only way this SDK should reach the network.
|
|
3
|
+
*
|
|
4
|
+
* `fetch` has NO timeout of its own: a server that accepts the connection and
|
|
5
|
+
* then never finishes the response leaves the promise pending forever, and the
|
|
6
|
+
* failure is silent (no rejection, no diagnostic, just an `await` that never
|
|
7
|
+
* resumes). Every network read therefore goes through here so a stalled server
|
|
8
|
+
* degrades into a fast, observable failure instead of a hang.
|
|
9
|
+
*
|
|
10
|
+
* Kotlin bounds the same reads with `connectTimeout`/`readTimeout`
|
|
11
|
+
* (`OverlayAdRenderer.openBoundedStream`) and Swift with a timeout-carrying
|
|
12
|
+
* `URLSession`; this is the web arm of that agreement.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Whole-call bound for short auxiliary reads (VAST tag, `Content-Type` probe).
|
|
16
|
+
* Matches Kotlin's `HTTP_TIMEOUT_MS`, so a stalled server fails after the same
|
|
17
|
+
* wall time on every platform. Deliberately far tighter than the manifest
|
|
18
|
+
* bound: these reads happen with a break already in flight, so the cut timer is
|
|
19
|
+
* the real deadline and a slow answer is worthless anyway.
|
|
20
|
+
*/
|
|
21
|
+
export declare const AUX_FETCH_TIMEOUT_MS = 5000;
|
|
22
|
+
/**
|
|
23
|
+
* Run `fetch` with a whole-call timeout.
|
|
24
|
+
*
|
|
25
|
+
* @param url resource to read.
|
|
26
|
+
* @param init standard fetch init; a caller-supplied `signal` is honoured in
|
|
27
|
+
* addition to the timeout (either one aborts the request).
|
|
28
|
+
* @param timeoutMs whole-call bound in milliseconds.
|
|
29
|
+
* @param timeoutMessage message for the thrown error when the bound is hit —
|
|
30
|
+
* an `AbortError` on its own reads like the caller cancelled, which sends the
|
|
31
|
+
* reader down the wrong path.
|
|
32
|
+
* @throws Error with `timeoutMessage` when the bound elapses; otherwise
|
|
33
|
+
* whatever `fetch` itself rejected with.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchWithTimeout(url: string, init?: RequestInit, timeoutMs?: number, timeoutMessage?: string): Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const AUX_FETCH_TIMEOUT_MS=5e3;export async function fetchWithTimeout(t,r={},e=5e3,o=`no response within ${e}ms`){const n=r.signal??void 0;if(n?.aborted){const t=new Error("The operation was aborted.");throw t.name="AbortError",t}const a=new AbortController,i=setTimeout(()=>a.abort(),e),s=()=>a.abort();n?.addEventListener("abort",s);try{return await fetch(t,{...r,signal:a.signal})}catch(t){if(a.signal.aborted&&!n?.aborted)throw new Error(o,{cause:t});throw t}finally{clearTimeout(i),n?.removeEventListener("abort",s)}}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which Lens ingest this build reports to — the whole switch is the re-export
|
|
3
|
+
* below, and `scripts/ci-stamp-lens-backend.sh` rewrites it during CI artefact
|
|
4
|
+
* builds (never committed, like the version stamp):
|
|
5
|
+
*
|
|
6
|
+
* main branch → production (committed default, no stamp)
|
|
7
|
+
* any other branch → development (rp2 ingest)
|
|
8
|
+
*
|
|
9
|
+
* The committed default is production on purpose: a build that somehow skips the
|
|
10
|
+
* stamp must degrade to "reports to production", never to "release artefact
|
|
11
|
+
* carrying the development URL".
|
|
12
|
+
*
|
|
13
|
+
* Not part of the public API surface — integrators cannot select, redirect or
|
|
14
|
+
* disable the Lens backend.
|
|
15
|
+
*/
|
|
16
|
+
export { configureLensBackend } from './lensBackendProduction';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{configureLensBackend}from"./lensBackendProduction";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function configureLensBackend(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function configureLensBackend(){throw new Error("OptiView Ads SDK: the development Lens backend is not part of production artefacts.")}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The production variant of the Lens backend seam: nothing to do. Lens's own
|
|
3
|
+
* built-in default is the production ingest, so this file deliberately imports
|
|
4
|
+
* no Lens module at all — which is what keeps the development ingest URL out of
|
|
5
|
+
* release artefacts (it only exists inside `@dolby-optiview/lens-sdk/dev`).
|
|
6
|
+
*
|
|
7
|
+
* The variant is chosen at build time; see `lensBackend.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function configureLensBackend(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function configureLensBackend(){}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Break Manifest signature verification (detached JWS, spec "Manifest
|
|
3
|
+
* signature" section).
|
|
4
|
+
*
|
|
5
|
+
* The backend signs every manifest response body and sends the signature in
|
|
6
|
+
* the `X-Manifest-Signature` header as a detached compact JWS (RFC 7515
|
|
7
|
+
* Appendix F) with an unencoded payload (RFC 7797, `b64:false`):
|
|
8
|
+
*
|
|
9
|
+
* <base64url(protected-header)>..<base64url(signature)>
|
|
10
|
+
*
|
|
11
|
+
* The signing input is `ASCII(base64url(protected-header)) || '.' || <body
|
|
12
|
+
* bytes>`, where the body bytes are the exact response body after transfer/
|
|
13
|
+
* content decoding — verified BEFORE the body is parsed as JSON.
|
|
14
|
+
*/
|
|
15
|
+
/** The header carrying the detached JWS over the manifest response body. */
|
|
16
|
+
export declare const MANIFEST_SIGNATURE_HEADER = "X-Manifest-Signature";
|
|
17
|
+
/** A trusted Ed25519 verification key, resolved by the protected `kid`. */
|
|
18
|
+
export interface ManifestSignatureKey {
|
|
19
|
+
kid: string;
|
|
20
|
+
/** Raw 32-byte Ed25519 public key. */
|
|
21
|
+
publicKey: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Why a manifest signature was rejected. The identifiers are shared verbatim
|
|
25
|
+
* across the TS/Kotlin/Swift cores and pinned by conformance fixtures.
|
|
26
|
+
*/
|
|
27
|
+
export type ManifestSignatureRejection = 'missing-header' | 'malformed-jws' | 'invalid-protected-header' | 'unsupported-alg' | 'invalid-b64' | 'unknown-critical' | 'unknown-kid' | 'signature-mismatch';
|
|
28
|
+
export type ManifestSignatureResult = {
|
|
29
|
+
valid: true;
|
|
30
|
+
kid: string;
|
|
31
|
+
} | {
|
|
32
|
+
valid: false;
|
|
33
|
+
reason: ManifestSignatureRejection;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Verify a detached JWS over the exact manifest response body bytes.
|
|
37
|
+
*
|
|
38
|
+
* Fail-closed: any missing, malformed, or unverifiable signature yields an
|
|
39
|
+
* invalid result with a specific rejection reason; only a signature by a key
|
|
40
|
+
* in `trustedKeys` (resolved by `kid`) validates. Unknown NON-critical
|
|
41
|
+
* protected-header parameters are ignored; unknown critical ones reject.
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyManifestSignature(signatureHeader: string | null | undefined, bodyBytes: Uint8Array, trustedKeys: readonly ManifestSignatureKey[]): ManifestSignatureResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ed25519Verify as e}from"./ed25519";export const MANIFEST_SIGNATURE_HEADER="X-Manifest-Signature";const r=new Set(["b64"]);function n(e){if(0===e.length||!/^[A-Za-z0-9_-]+$/.test(e))return null;const r=e.replace(/-/g,"+").replace(/_/g,"/");try{const e=atob(r),n=new Uint8Array(e.length);for(let r=0;r<e.length;r++)n[r]=e.charCodeAt(r);return n}catch{return null}}export function verifyManifestSignature(t,i,a){if(null==t||""===t.trim())return{valid:!1,reason:"missing-header"};const o=t.trim().split(".");if(3!==o.length||""!==o[1])return{valid:!1,reason:"malformed-jws"};const[l,,s]=o,d=n(l),c=n(s);if(!d||!c||64!==c.length)return{valid:!1,reason:"malformed-jws"};let u;try{u=JSON.parse((new TextDecoder).decode(d))}catch{return{valid:!1,reason:"invalid-protected-header"}}if("object"!=typeof u||null===u||Array.isArray(u))return{valid:!1,reason:"invalid-protected-header"};const f=u;if("EdDSA"!==f.alg)return{valid:!1,reason:"unsupported-alg"};const g=f.crit;if(!Array.isArray(g)||!g.includes("b64")||!1!==f.b64)return{valid:!1,reason:"invalid-b64"};for(const e of g)if("string"!=typeof e||!r.has(e))return{valid:!1,reason:"unknown-critical"};const h=f.kid;if("string"!=typeof h)return{valid:!1,reason:"invalid-protected-header"};const v=a.find(e=>e.kid===h);if(!v)return{valid:!1,reason:"unknown-kid"};const p=`${l}.`,y=new Uint8Array(p.length+i.length);for(let e=0;e<p.length;e++)y[e]=p.charCodeAt(e);return y.set(i,p.length),e(y,c,v.publicKey)?{valid:!0,kid:h}:{valid:!1,reason:"signature-mismatch"}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Break Manifest trust list this build embeds: unconditionally the three
|
|
3
|
+
* production backend keys (`../config/keys.ts`) for every published artifact.
|
|
4
|
+
* This module stays a separate seam so package-internal test tooling (the
|
|
5
|
+
* core Jest config and the private demo/test-app Vite configs) can alias it
|
|
6
|
+
* to a test-only provider; nothing selects a different list at build or run
|
|
7
|
+
* time otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export { embeddedManifestKeys } from '../config/keys';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{embeddedManifestKeys}from"../config/keys";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BreakManifest } from '../types/BreakManifest';
|
|
2
|
+
/**
|
|
3
|
+
* Supported major version of the break manifest.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SUPPORTED_MANIFEST_MAJOR_VERSION = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Parse and validate raw break-manifest data into a {@link BreakManifest}.
|
|
8
|
+
*
|
|
9
|
+
* Pure (no I/O): callers fetch the bytes and pass the decoded JSON. Shared by
|
|
10
|
+
* the client `ManifestService` and the server-side SSAI stitcher so both apply
|
|
11
|
+
* identical version/timebase/breaks validation.
|
|
12
|
+
*
|
|
13
|
+
* @throws Error when the data is not a valid v1 break manifest.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseBreakManifest(data: unknown): BreakManifest;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SUPPORTED_MANIFEST_MAJOR_VERSION=1;export function parseBreakManifest(r){if(!r||"object"!=typeof r)throw new Error("Invalid manifest: expected object");const t=r;if("string"!=typeof t.version)throw new Error("Invalid manifest: missing version");if(1!==parseInt(t.version.split(".")[0],10))throw new Error(`Unsupported manifest version: ${t.version}. Supported major version: 1`);const n=t.timebase;if("wallclock"!==n&&"pts"!==n&&"mediatime"!==n)throw new Error("Invalid manifest: invalid or missing timebase");if(!Array.isArray(t.breaks))throw new Error("Invalid manifest: breaks must be an array");for(const r of t.breaks)e(r);if(void 0!==t.delivery){if(!Array.isArray(t.delivery)||0===t.delivery.length)throw new Error("Invalid manifest: delivery must be a non-empty array");for(const e of t.delivery)if(!e||"object"!=typeof e||"string"!=typeof e.mode)throw new Error("Invalid manifest: each delivery rule must have a string mode")}if(void 0!==t.vendorConfiguration&&!Array.isArray(t.vendorConfiguration))throw new Error("Invalid manifest: vendorConfiguration must be an array");if(void 0!==t.adStartDelay&&("number"!=typeof t.adStartDelay||!Number.isFinite(t.adStartDelay)||t.adStartDelay<0))throw new Error("Invalid manifest: adStartDelay must be a number >= 0");return t}function e(e){if(!e||"object"!=typeof e)return;const r=e.start;if(!r||"object"!=typeof r)return;const t=r;if("event"!==t.type||"string"!=typeof t.event)throw new Error('Invalid manifest: break start object must be { type: "event", event: string }');if(void 0!==t.delay&&("number"!=typeof t.delay||!Number.isFinite(t.delay)||t.delay<0))throw new Error("Invalid manifest: event trigger delay must be a number >= 0")}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable VAST `CompanionAds` parser for pause ads.
|
|
3
|
+
*
|
|
4
|
+
* Pause ads use a **static image** companion creative, not a linear video, so we
|
|
5
|
+
* do not need Google IMA: we extract the image directly from the VAST XML. This
|
|
6
|
+
* parser is part of the portable brain (locked by the cross-language conformance
|
|
7
|
+
* harness), so it must run identically in the browser and under Node — it is
|
|
8
|
+
* therefore **dependency-free** and does not rely on `DOMParser`. It performs a
|
|
9
|
+
* tolerant, targeted extraction of the `CompanionAds` shape rather than full
|
|
10
|
+
* VAST parsing.
|
|
11
|
+
*/
|
|
12
|
+
/** The image creative extracted from a VAST `CompanionAds` response. */
|
|
13
|
+
export interface VastCompanionImage {
|
|
14
|
+
/** The `StaticResource` image URL. */
|
|
15
|
+
imageUrl: string;
|
|
16
|
+
/** `CompanionClickThrough` navigation target, when present. */
|
|
17
|
+
clickThrough?: string;
|
|
18
|
+
/** `creativeView` tracking URLs, fired once when the image is displayed. */
|
|
19
|
+
impressions: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extract the first image companion from a VAST document.
|
|
23
|
+
*
|
|
24
|
+
* Reads the **first** `CompanionAds/Companion` whose `StaticResource` has an
|
|
25
|
+
* `image/*` `creativeType`, plus its optional `CompanionClickThrough` and
|
|
26
|
+
* `creativeView` tracking. Returns `null` when no image companion is found (the
|
|
27
|
+
* caller then skips the pause ad with a diagnostic).
|
|
28
|
+
*
|
|
29
|
+
* @param xml - The raw VAST XML string.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseVastCompanionImage(xml: string): VastCompanionImage | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){const n=/<!\[CDATA\[([\s\S]*?)\]\]>/.exec(e);return(n?n[1]:e).trim()}export function parseVastCompanionImage(n){if(!n||"string"!=typeof n)return null;const i=/<Companion\b[^>]*>([\s\S]*?)<\/Companion>/gi;let o;for(;null!==(o=i.exec(n));){const n=o[1],i=/<StaticResource\b([^>]*)>([\s\S]*?)<\/StaticResource>/i.exec(n);if(!i)continue;const t=i[1],c=/creativeType\s*=\s*["']([^"']+)["']/i.exec(t);if(!c||!/^image\//i.test(c[1]))continue;const s=e(i[2]);if(!s)continue;const r=/<CompanionClickThrough\b[^>]*>([\s\S]*?)<\/CompanionClickThrough>/i.exec(n),u=r?e(r[1]):"",a=[],l=/<Tracking\b[^>]*\bevent\s*=\s*["']creativeView["'][^>]*>([\s\S]*?)<\/Tracking>/gi;let g;for(;null!==(g=l.exec(n));){const n=e(g[1]);n&&a.push(n)}return{imageUrl:s,...u?{clickThrough:u}:{},impressions:a}}return null}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable VAST `Linear` MediaFile parser for **video** pause ads.
|
|
3
|
+
*
|
|
4
|
+
* A video pause ad may be sourced from a VAST `InLine` whose `Creative` carries
|
|
5
|
+
* a `Linear` element with one or more progressive `MediaFile`s. Unlike a linear
|
|
6
|
+
* playout ad (which would go through Google IMA), a pause ad just needs a
|
|
7
|
+
* directly-playable MP4 URL that the render layer can attach to a muted,
|
|
8
|
+
* play-once `<video>`/AVPlayer/ExoPlayer. We therefore extract the MediaFile URL
|
|
9
|
+
* directly from the VAST XML rather than invoking IMA.
|
|
10
|
+
*
|
|
11
|
+
* Like {@link ./parseVastCompanion}, this parser is part of the portable brain
|
|
12
|
+
* (locked by the cross-language conformance harness), so it must run identically
|
|
13
|
+
* in the browser and under Node — it is **dependency-free** and does not rely on
|
|
14
|
+
* `DOMParser`. It performs a tolerant, targeted extraction of the `Linear`
|
|
15
|
+
* `MediaFile` shape rather than full VAST parsing.
|
|
16
|
+
*/
|
|
17
|
+
/** The video creative extracted from a VAST `Linear` response. */
|
|
18
|
+
export interface VastLinearMediaFile {
|
|
19
|
+
/** The progressive `MediaFile` MP4 URL. */
|
|
20
|
+
mediaFileUrl: string;
|
|
21
|
+
/** `VideoClicks/ClickThrough` navigation target, when present. */
|
|
22
|
+
clickThrough?: string;
|
|
23
|
+
/** `Impression` URLs, fired once when the video is displayed. */
|
|
24
|
+
impressions: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract the first progressive MP4 media file from a VAST document.
|
|
28
|
+
*
|
|
29
|
+
* Reads the **first** `MediaFile` whose `delivery` is `progressive` and whose
|
|
30
|
+
* `type` is `video/mp4`, plus the optional `VideoClicks/ClickThrough` and all
|
|
31
|
+
* `Impression` URLs. Returns `null` when no usable MP4 media file is found (the
|
|
32
|
+
* caller then skips the pause ad with a diagnostic).
|
|
33
|
+
*
|
|
34
|
+
* @param xml - The raw VAST XML string.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseVastLinearMediaFile(xml: string): VastLinearMediaFile | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){const i=/<!\[CDATA\[([\s\S]*?)\]\]>/.exec(e);return(i?i[1]:e).trim()}export function parseVastLinearMediaFile(i){if(!i||"string"!=typeof i)return null;const s=/<MediaFile\b([^>]*)>([\s\S]*?)<\/MediaFile>/gi;let n,r="";for(;null!==(n=s.exec(i));){const i=n[1],s=/delivery\s*=\s*["']([^"']+)["']/i.exec(i),t=/type\s*=\s*["']([^"']+)["']/i.exec(i);if(!s||!/progressive/i.test(s[1]))continue;if(!t||!/^video\/mp4\b/i.test(t[1].trim()))continue;const o=e(n[2]);if(o){r=o;break}}if(!r)return null;const t=/<ClickThrough\b[^>]*>([\s\S]*?)<\/ClickThrough>/i.exec(i),o=t?e(t[1]):"",c=[],l=/<Impression\b[^>]*>([\s\S]*?)<\/Impression>/gi;let u;for(;null!==(u=l.exec(i));){const i=e(u[1]);i&&c.push(i)}return{mediaFileUrl:r,...o?{clickThrough:o}:{},impressions:c}}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BreakFormat } from '../types/BreakManifest';
|
|
2
|
+
import type { Break } from '../types/BreakManifest';
|
|
3
|
+
/**
|
|
4
|
+
* True when `declared` would be overruled by PiP — i.e. the caller must also drop the
|
|
5
|
+
* companion, play a single asset, and report `single` on the public events.
|
|
6
|
+
*/
|
|
7
|
+
export declare function pipOverridesFormat(declared: BreakFormat | undefined, pictureInPicture: boolean): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* True when `declared` is an {@link BreakFormat.LSHAPE_CONTENT} break playing in PiP, where
|
|
10
|
+
* the window shows the main content: the caller must NOT draw the backdrop and must NOT
|
|
11
|
+
* reframe content into its corner box.
|
|
12
|
+
*
|
|
13
|
+
* This format cannot follow the `single` rule the others do — it carries no fullscreen video
|
|
14
|
+
* ad to promote, only decoration around content that keeps playing. Squeezed into a PiP
|
|
15
|
+
* window that decoration is unreadable, and shrinking content inside an already tiny window
|
|
16
|
+
* leaves the viewer with almost nothing. So the break still runs (it keeps its declared
|
|
17
|
+
* format and its duration on the public events); it simply renders nothing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function pipSuppressesBackdrop(declared: BreakFormat | undefined, pictureInPicture: boolean): boolean;
|
|
20
|
+
/** `declared` as it will ACTUALLY play, given the current PiP state. */
|
|
21
|
+
export declare function effectiveFormat(declared: BreakFormat | undefined, pictureInPicture: boolean): BreakFormat | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The format `breakInfo` will ACTUALLY play as — {@link resolveBreakFormat} with the PiP
|
|
24
|
+
* override applied. This is what the public events report, so a consumer laying out its own
|
|
25
|
+
* UI sees the format the viewer is really getting rather than the one the manifest asked
|
|
26
|
+
* for. The declared format is not lost: `DA-PIP-FORMAT-OVERRIDE` records both.
|
|
27
|
+
*/
|
|
28
|
+
export declare function effectiveBreakFormat(breakInfo: Break, pictureInPicture: boolean): BreakFormat | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{BreakFormat as e}from"../types/BreakManifest";import{resolveBreakFormat as r}from"./AdBreakSequencer";const t=[e.DOUBLE,e.LSHAPE_AD,e.OVERLAY];export function pipOverridesFormat(e,r){return r&&void 0!==e&&t.includes(e)}export function pipSuppressesBackdrop(r,t){return t&&r===e.LSHAPE_CONTENT}export function effectiveFormat(r,t){return pipOverridesFormat(r,t)?e.SINGLE:r}export function effectiveBreakFormat(e,t){return effectiveFormat(r(e),t)}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge asset parameters in conformance-locked order, lowest to highest:
|
|
3
|
+
* SessionConfig < updateAssetParameters() < manifest session < per-asset.
|
|
4
|
+
* Each layer overwrites earlier values per key; undefined is returned when all layers are empty.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveAssetParameters(sessionConfigParameters: Record<string, string> | undefined, updateParameters: Record<string, string> | undefined, manifestSessionParameters: Record<string, string> | undefined, assetParameters: Record<string, string> | undefined): Record<string, string> | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolveAssetParameters(e,t,r,s){const n={...e??{},...t??{},...r??{},...s??{}};return Object.keys(n).length>0?n:void 0}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BreakManifest, DeliveryMode, DeliveryRule, DeviceType, VendorConfiguration } from '../types/BreakManifest';
|
|
2
|
+
import { type StitchedStreamVendor } from './resolveGamVendorConfiguration';
|
|
3
|
+
export type { StitchedStreamVendor } from './resolveGamVendorConfiguration';
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when the manifest steers the session to ssai but no vendor
|
|
6
|
+
* configuration can provide a stitched stream. Deliberately catchable by the
|
|
7
|
+
* application (no silent fallback to sgai): the app decides which stream to
|
|
8
|
+
* play instead.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SsaiUnavailableError extends Error {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Outcome of the once-per-session delivery-mode resolution.
|
|
15
|
+
*/
|
|
16
|
+
export interface DeliveryModeDecision {
|
|
17
|
+
mode: DeliveryMode;
|
|
18
|
+
/** Index of the delivery rule that decided the mode, or null when none matched. */
|
|
19
|
+
ruleIndex: number | null;
|
|
20
|
+
/**
|
|
21
|
+
* Set when the matching rule carried an unrecognized mode value: the raw
|
|
22
|
+
* value, which the resolver replaced with sgai (forward compatibility).
|
|
23
|
+
*/
|
|
24
|
+
unknownMode: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the delivery mode from a manifest's ordered `delivery` rules for the
|
|
28
|
+
* requesting device, as a pure function shared (by behavioural contract) with
|
|
29
|
+
* the Kotlin and Swift cores and locked by the cross-language conformance
|
|
30
|
+
* harness.
|
|
31
|
+
*
|
|
32
|
+
* Selection order:
|
|
33
|
+
* 1. Rules are evaluated in array order; the first rule whose
|
|
34
|
+
* `targeting.deviceType` matches `deviceType` — or that carries no
|
|
35
|
+
* `targeting.deviceType` (the manifest's default rule) — wins.
|
|
36
|
+
* 2. No `delivery` array, or no matching rule → sgai (preserves the pre-steering
|
|
37
|
+
* behaviour).
|
|
38
|
+
* 3. A matching rule with an unrecognized mode value → sgai, with the raw value
|
|
39
|
+
* reported via `unknownMode` so the caller can log it (forward compatibility).
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveDeliveryMode(delivery: DeliveryRule[] | undefined, deviceType: DeviceType): DeliveryModeDecision;
|
|
42
|
+
/**
|
|
43
|
+
* Find the first vendor configuration entry able to provide a server-stitched
|
|
44
|
+
* stream — currently a `gam` entry carrying a full-service `assetKey`. Returns
|
|
45
|
+
* null when the manifest offers none, in which case an ssai session cannot
|
|
46
|
+
* start (the SDK throws; no silent fallback to sgai).
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveStitchedStreamVendor(vendorConfiguration: VendorConfiguration[] | undefined): StitchedStreamVendor | null;
|
|
49
|
+
/**
|
|
50
|
+
* Strip timebased breaks from a manifest for a manifest-steered ssai session:
|
|
51
|
+
* the stitched stream carries those server-side, while event-triggered
|
|
52
|
+
* (spec `start.type: 'event'`, or positional pre) breaks remain scheduled and rendered client-side.
|
|
53
|
+
*/
|
|
54
|
+
export declare function filterSsaiClientBreaks(manifest: BreakManifest): BreakManifest;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DeliveryMode as e,getBreakEvent as r}from"../types/BreakManifest";import{resolveGamVendorConfiguration as n}from"./resolveGamVendorConfiguration";export class SsaiUnavailableError extends Error{constructor(e){super(e),this.name="SsaiUnavailableError"}}export function resolveDeliveryMode(r,n){if(!Array.isArray(r))return{mode:e.SGAI,ruleIndex:null,unknownMode:null};for(let o=0;o<r.length;o++){const t=r[o],i=t?.targeting?.deviceType;if(void 0===i||i===n)return t.mode===e.SGAI||t.mode===e.SSAI?{mode:t.mode,ruleIndex:o,unknownMode:null}:{mode:e.SGAI,ruleIndex:o,unknownMode:String(t.mode)}}return{mode:e.SGAI,ruleIndex:null,unknownMode:null}}export function resolveStitchedStreamVendor(e){return n(void 0===e?{}:{vendorConfiguration:e}).ssai}export function filterSsaiClientBreaks(e){return{...e,breaks:e.breaks.filter(e=>null!==r(e))}}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { BreakManifest } from '../types/BreakManifest';
|
|
2
|
+
/**
|
|
3
|
+
* GAM stream identity for the IMA/DAI session. Both required: a blank value yields a pod URL like
|
|
4
|
+
* `.../network//custom_asset/...` whose 404 reads as a GAM outage instead of a config error.
|
|
5
|
+
*/
|
|
6
|
+
export interface GamSessionIdentity {
|
|
7
|
+
networkCode: string;
|
|
8
|
+
customAssetKey: string;
|
|
9
|
+
}
|
|
10
|
+
export interface StitchedStreamVendor {
|
|
11
|
+
networkCode: string;
|
|
12
|
+
assetKey: string;
|
|
13
|
+
assetParameters: Record<string, string> | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Why no identity could be resolved. `ok` means one was. */
|
|
16
|
+
export type GamVendorConfigurationReason = 'ok' | 'absent' | 'missing-network-code' | 'missing-custom-asset-key';
|
|
17
|
+
export interface GamVendorConfigurationResult {
|
|
18
|
+
/** The resolved identity, or null when `reason !== 'ok'`. */
|
|
19
|
+
identity: GamSessionIdentity | null;
|
|
20
|
+
reason: GamVendorConfigurationReason;
|
|
21
|
+
/** Unrecognised `type` values, in manifest order, deduped. Ignored per the forward-compat rule. */
|
|
22
|
+
unknownTypes: string[];
|
|
23
|
+
/** True when more than one `type: 'gam'` entry was present; the first one won. */
|
|
24
|
+
duplicateGam: boolean;
|
|
25
|
+
sgaiAssetParameters: Record<string, string> | undefined;
|
|
26
|
+
ssai: StitchedStreamVendor | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The ONLY place any core reads `vendorConfiguration`. Validated defensively from the typed
|
|
30
|
+
* model: `parseBreakManifest` ends in a pass-through cast, while the Kotlin/Swift parsers build
|
|
31
|
+
* field by field, so this keeps the three cores answering identically. Spec: at most one entry
|
|
32
|
+
* per type (FIRST `gam` wins, duplicate reported), unknown types ignored and collected.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveGamVendorConfiguration(manifest: Pick<BreakManifest, 'vendorConfiguration'>): GamVendorConfigurationResult;
|
|
35
|
+
/** One field on which a pod asset disagrees with the session it would be served under. */
|
|
36
|
+
export interface GamPodIdentityMismatch {
|
|
37
|
+
field: 'networkCode' | 'customAssetKey';
|
|
38
|
+
assetValue: string;
|
|
39
|
+
sessionValue: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fields on which a pod asset's `vendorParameters` disagree with the session identity
|
|
43
|
+
* (`networkCode` first). The pod URL carries the ASSET's identity while `stream_id` was
|
|
44
|
+
* registered under the session's; Google answers a crossed pair with HTTP 410 Gone (measured
|
|
45
|
+
* 2026-08-27), so a mismatch is a provisioning bug and the caller reports both values.
|
|
46
|
+
*/
|
|
47
|
+
export declare function gamPodIdentityMismatches(session: GamSessionIdentity, assetParams: {
|
|
48
|
+
networkCode: string;
|
|
49
|
+
customAssetKey: string;
|
|
50
|
+
}): GamPodIdentityMismatch[];
|
|
51
|
+
/**
|
|
52
|
+
* GAM is activated by the MANIFEST, not client config. A manifest without gam
|
|
53
|
+
* `vendorConfiguration` is normal when it has no GAM assets, so a missing identity is only worth reporting when one is needed.
|
|
54
|
+
*/
|
|
55
|
+
export declare function manifestHasGamAssets(manifest: Pick<BreakManifest, 'breaks'>): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return"string"==typeof e&&""!==e.trim()?e:null}export function resolveGamVendorConfiguration(t){const n=t.vendorConfiguration,o={identity:null,unknownTypes:[],duplicateGam:!1,sgaiAssetParameters:void 0,ssai:null};if(!Array.isArray(n))return{...o,reason:"absent"};const r=[];let a=null,i=0;for(const s of n){if("object"!=typeof s||null===s)continue;const t=s,n=e(t.type);null!==n&&("gam"!==n?r.includes(n)||r.push(n):(i+=1,null===a&&(a=t)))}const u=i>1;if(null===a)return{...o,reason:"absent",unknownTypes:r,duplicateGam:u};const l=e(a.networkCode);if(null===l)return{...o,reason:"missing-network-code",unknownTypes:r,duplicateGam:u};const c=Array.isArray(a.ssai)?a.ssai[0]:void 0,y=("object"==typeof c&&null!==c?e(c.assetKey):null)??e(a.assetKey),f=y?{networkCode:l,assetKey:y,assetParameters:s("object"==typeof c&&null!==c?c.assetParameters:void 0)}:null,d=Array.isArray(a.sgai)?a.sgai[0]:void 0,m=("object"==typeof d&&null!==d?e(d.customAssetKey):null)??e(a.customAssetKey);if(null===m)return{...o,reason:"missing-custom-asset-key",unknownTypes:r,duplicateGam:u,ssai:f};return{identity:{networkCode:l,customAssetKey:m},reason:"ok",unknownTypes:r,duplicateGam:u,sgaiAssetParameters:s("object"==typeof d&&null!==d?d.assetParameters:void 0),ssai:f}}function s(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const s=Object.fromEntries(Object.entries(e).filter(([,e])=>"string"==typeof e));return Object.keys(s).length>0?s:void 0}export function gamPodIdentityMismatches(e,s){const t=[];return s.networkCode!==e.networkCode&&t.push({field:"networkCode",assetValue:s.networkCode,sessionValue:e.networkCode}),s.customAssetKey!==e.customAssetKey&&t.push({field:"customAssetKey",assetValue:s.customAssetKey,sessionValue:e.customAssetKey}),t}export function manifestHasGamAssets(e){for(const s of e.breaks??[]){const e=[...s.variants??[],...s.variant?[s.variant]:[]];for(const s of e)for(const e of s?.assets??[])if("gam"===e?.vendor)return!0}return!1}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where content should resume after a pause ad, as a pure function shared (by
|
|
3
|
+
* behavioural contract) with the Kotlin and Swift cores and locked by the
|
|
4
|
+
* cross-language conformance harness.
|
|
5
|
+
*
|
|
6
|
+
* A pause ad holds the viewer at a fixed position while the stream keeps
|
|
7
|
+
* advancing. On a live stream with a DVR window that position is only playable
|
|
8
|
+
* for as long as it stays inside the window — pause for longer than the DVR
|
|
9
|
+
* depth and it falls off the back, at which point resuming "where you were" is
|
|
10
|
+
* not a choice the player can honour.
|
|
11
|
+
*
|
|
12
|
+
* So there are exactly two outcomes:
|
|
13
|
+
*
|
|
14
|
+
* 1. **The pinned position is still inside the window** → `null`, meaning
|
|
15
|
+
* *resume in place with no seek*. This is the normal case and the one the
|
|
16
|
+
* viewer expects: press play, continue exactly where you paused. Returning
|
|
17
|
+
* `null` rather than the pinned position is deliberate — it tells the caller
|
|
18
|
+
* not to seek at all, so a stream that never lost its position is never
|
|
19
|
+
* nudged by a redundant seek.
|
|
20
|
+
* 2. **The pinned position has dropped below the window's start** → the live
|
|
21
|
+
* edge, because the paused content no longer exists to resume into.
|
|
22
|
+
*
|
|
23
|
+
* VOD needs no special case: a VOD asset's seekable range starts at 0 and the
|
|
24
|
+
* pinned position can never fall below it, so branch 1 always applies.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolvePauseResumePoint(
|
|
27
|
+
/** `currentTime` captured when the pause ad appeared. */
|
|
28
|
+
pinnedPosition: number,
|
|
29
|
+
/** `seekable.start(0)` now — the back edge of the DVR window. */
|
|
30
|
+
seekableStart: number,
|
|
31
|
+
/** `seekable.end(len - 1)` now — the live edge. */
|
|
32
|
+
seekableEnd: number): number | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolvePauseResumePoint(e,i,n){return Number.isFinite(e)&&Number.isFinite(i)?!Number.isFinite(n)||n<=i||e>=i?null:n:null}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Polling } from '../types/BreakManifest';
|
|
2
|
+
/**
|
|
3
|
+
* The polling behaviour the manifest dictates for the current moment.
|
|
4
|
+
*
|
|
5
|
+
* - `once` — the manifest carries no `polling` object, so it is fetched
|
|
6
|
+
* exactly once per session and never re-polled.
|
|
7
|
+
* - `poll` — re-fetch on a timer, using the manifest's own `active` interval
|
|
8
|
+
* while an ad break is active and its `idle` interval otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export type PollingDecision = {
|
|
11
|
+
mode: 'once';
|
|
12
|
+
} | {
|
|
13
|
+
mode: 'poll';
|
|
14
|
+
intervalSeconds: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Pure spec-compliant cadence resolution (shared brain, conformance-tested).
|
|
18
|
+
*
|
|
19
|
+
* The manifest is the only source of the polling cadence: there is no client
|
|
20
|
+
* default and no configuration override. Callers re-resolve whenever the
|
|
21
|
+
* active state flips or a freshly polled manifest changes/removes `polling`,
|
|
22
|
+
* and restart their timer when the decision changes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolvePollingDecision(polling: Polling | undefined, active: boolean): PollingDecision;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolvePollingDecision(e,o){return e?{mode:"poll",intervalSeconds:o?e.active:e.idle}:{mode:"once"}}
|