@trillboards/ads-sdk 2.1.1 → 2.3.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.
@@ -42,9 +42,11 @@ interface TrillboardsState {
42
42
  programmaticPlaying: boolean;
43
43
  prefetchedReady: boolean;
44
44
  waterfallMode: WaterfallMode;
45
+ adDeliveryProfileMode: AdDeliveryMode | null;
45
46
  screenId: string | null;
46
47
  deviceId: string | null;
47
48
  }
49
+ type AdDeliveryMode = 'ima_sdk' | 'vast_fallback';
48
50
  interface EventMap {
49
51
  initialized: {
50
52
  deviceId: string;
@@ -42,9 +42,11 @@ interface TrillboardsState {
42
42
  programmaticPlaying: boolean;
43
43
  prefetchedReady: boolean;
44
44
  waterfallMode: WaterfallMode;
45
+ adDeliveryProfileMode: AdDeliveryMode | null;
45
46
  screenId: string | null;
46
47
  deviceId: string | null;
47
48
  }
49
+ type AdDeliveryMode = 'ima_sdk' | 'vast_fallback';
48
50
  interface EventMap {
49
51
  initialized: {
50
52
  deviceId: string;
@@ -154,6 +154,7 @@ var DEFAULT_PUBLIC_STATE = {
154
154
  programmaticPlaying: false,
155
155
  prefetchedReady: false,
156
156
  waterfallMode: "programmatic_only",
157
+ adDeliveryProfileMode: null,
157
158
  screenId: null,
158
159
  deviceId: null
159
160
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-native/TrillboardsWebView.tsx","../src/core/state.ts","../src/react-native/useTrillboardsNative.ts"],"names":["forwardRef","useRef","useState","useCallback","useImperativeHandle","jsx"],"mappings":"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","file":"react-native.js","sourcesContent":["import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\n\n// Type-only imports to avoid requiring react-native at build time\ntype WebViewType = any;\n\n// Lazy-load react-native-webview once, outside of render\nlet _WebView: any = undefined;\nfunction getWebView(): any {\n if (_WebView === undefined) {\n try {\n _WebView = require('react-native-webview').default;\n } catch {\n _WebView = null;\n }\n }\n return _WebView;\n}\n\ninterface TrillboardsWebViewProps {\n deviceId: string;\n waterfall?: WaterfallMode;\n autoStart?: boolean;\n apiBase?: string;\n style?: any;\n onReady?: () => void;\n onAdStarted?: (data: EventMap['ad_started']) => void;\n onAdEnded?: (data: EventMap['ad_ended']) => void;\n onAdError?: (data: EventMap['ad_error']) => void;\n onProgrammaticStarted?: (data: EventMap['programmatic_started']) => void;\n onProgrammaticEnded?: (data: EventMap['programmatic_ended']) => void;\n onStateChanged?: (state: TrillboardsState) => void;\n}\n\nexport interface TrillboardsWebViewHandle {\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n getState: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n}\n\nconst EMBED_BASE_URL = 'https://screen.trillboards.com/partner-sdk/embed.html';\n\nexport const TrillboardsWebView = forwardRef<TrillboardsWebViewHandle, TrillboardsWebViewProps>(\n (\n {\n deviceId,\n waterfall = 'programmatic_only',\n autoStart = false,\n apiBase,\n style,\n onReady,\n onAdStarted,\n onAdEnded,\n onAdError,\n onProgrammaticStarted,\n onProgrammaticEnded,\n onStateChanged,\n },\n ref\n ) => {\n const webViewRef = useRef<WebViewType>(null);\n const [isReady, setIsReady] = useState(false);\n\n // Store callbacks in refs to prevent unnecessary WebView re-renders\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onAdStartedRef = useRef(onAdStarted);\n onAdStartedRef.current = onAdStarted;\n const onAdEndedRef = useRef(onAdEnded);\n onAdEndedRef.current = onAdEnded;\n const onAdErrorRef = useRef(onAdError);\n onAdErrorRef.current = onAdError;\n const onProgrammaticStartedRef = useRef(onProgrammaticStarted);\n onProgrammaticStartedRef.current = onProgrammaticStarted;\n const onProgrammaticEndedRef = useRef(onProgrammaticEnded);\n onProgrammaticEndedRef.current = onProgrammaticEnded;\n const onStateChangedRef = useRef(onStateChanged);\n onStateChangedRef.current = onStateChanged;\n\n // Build embed URL\n const embedUrl = (() => {\n const params = new URLSearchParams({\n device_id: deviceId,\n waterfall,\n auto_start: autoStart ? '1' : '0',\n });\n if (apiBase) params.set('api_base', apiBase);\n return `${EMBED_BASE_URL}?${params.toString()}`;\n })();\n\n // Send command to WebView — use JSON.stringify for both action and params to prevent XSS\n const sendCommand = useCallback(\n (action: string, params: Record<string, unknown> = {}) => {\n if (!webViewRef.current) return;\n const js = `\n window.postMessage({\n type: 'trillboards-command',\n action: ${JSON.stringify(action)},\n params: ${JSON.stringify(params)}\n }, '*');\n true;\n `;\n webViewRef.current.injectJavaScript(js);\n },\n []\n );\n\n // Expose imperative handle\n useImperativeHandle(\n ref,\n () => ({\n show: () => sendCommand('show'),\n hide: () => sendCommand('hide'),\n skipAd: () => sendCommand('skip'),\n refresh: () => sendCommand('refresh'),\n getState: () => sendCommand('getState'),\n configure: (params) => sendCommand('configure', params),\n }),\n [sendCommand]\n );\n\n // Handle messages from WebView — use refs so dependency array is stable\n const handleMessage = useCallback(\n (event: any) => {\n try {\n const data = typeof event.nativeEvent.data === 'string'\n ? JSON.parse(event.nativeEvent.data)\n : event.nativeEvent.data;\n\n if (data.type !== 'trillboards') return;\n\n switch (data.event) {\n case 'initialized':\n setIsReady(true);\n onReadyRef.current?.();\n break;\n case 'ad_started':\n onAdStartedRef.current?.(data.data);\n break;\n case 'ad_ended':\n onAdEndedRef.current?.(data.data);\n break;\n case 'ad_error':\n onAdErrorRef.current?.(data.data);\n break;\n case 'programmatic_started':\n onProgrammaticStartedRef.current?.(data.data);\n break;\n case 'programmatic_ended':\n onProgrammaticEndedRef.current?.(data.data);\n break;\n case 'state_changed':\n onStateChangedRef.current?.(data.data);\n break;\n }\n } catch {\n // Ignore non-JSON messages\n }\n },\n []\n );\n\n const WebView = getWebView();\n if (!WebView) return null;\n\n return (\n <WebView\n ref={webViewRef}\n source={{ uri: embedUrl }}\n style={[{ flex: 1, backgroundColor: '#000' }, style]}\n onMessage={handleMessage}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n mediaPlaybackRequiresUserAction={false}\n allowsInlineMediaPlayback={true}\n mixedContentMode=\"compatibility\"\n originWhitelist={['https://*', 'http://*']}\n />\n );\n }\n);\n\nTrillboardsWebView.displayName = 'TrillboardsWebView';\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Internal + public state management\n// ─────────────────────────────────────────────────────────────\n\nimport type {\n AdItem,\n TrillboardsState,\n WaterfallMode,\n ProgrammaticSettings,\n ScreenDimensions,\n} from './types';\n\n/**\n * The full internal state held by the SDK engine.\n * This is NOT exposed to consumers — they receive the\n * trimmed-down `TrillboardsState` via `getPublicState()`.\n */\nexport interface InternalState {\n deviceId: string | null;\n partnerId: string | null;\n screenId: string | null;\n ads: AdItem[];\n currentAdIndex: number;\n isPlaying: boolean;\n isPaused: boolean;\n isOffline: boolean;\n settings: Record<string, unknown>;\n programmatic: ProgrammaticSettings | null;\n programmaticPlaying: boolean;\n prefetchedReady: boolean;\n waterfallMode: WaterfallMode;\n autoStart: boolean;\n container: HTMLElement | null;\n adTimer: ReturnType<typeof setTimeout> | null;\n refreshTimer: ReturnType<typeof setInterval> | null;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n programmaticRetryTimer: ReturnType<typeof setTimeout> | null;\n programmaticRetryActive: boolean;\n programmaticRetryCount: number;\n programmaticLastError: string | null;\n initialized: boolean;\n screenOrientation: string | null;\n screenDimensions: ScreenDimensions | null;\n etag: string | null;\n}\n\n/**\n * Default public state used by React components and the IIFE\n * entry point before the SDK is initialized.\n */\nexport const DEFAULT_PUBLIC_STATE: TrillboardsState = {\n initialized: false,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n currentAd: null,\n adCount: 0,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n screenId: null,\n deviceId: null,\n};\n\n/**\n * Create a fresh internal state object with safe defaults.\n * Called once during SDK initialisation.\n */\nexport function createInitialState(): InternalState {\n return {\n deviceId: null,\n partnerId: null,\n screenId: null,\n ads: [],\n currentAdIndex: 0,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n settings: {},\n programmatic: null,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n autoStart: true,\n container: null,\n adTimer: null,\n refreshTimer: null,\n heartbeatTimer: null,\n programmaticRetryTimer: null,\n programmaticRetryActive: false,\n programmaticRetryCount: 0,\n programmaticLastError: null,\n initialized: false,\n screenOrientation: null,\n screenDimensions: null,\n etag: null,\n };\n}\n\n/**\n * Project the internal state into a safe, read-only snapshot\n * that can be handed to consumers and event listeners.\n */\nexport function getPublicState(internal: InternalState): TrillboardsState {\n return {\n initialized: internal.initialized,\n isPlaying: internal.isPlaying,\n isPaused: internal.isPaused,\n isOffline: internal.isOffline,\n currentAd: internal.ads[internal.currentAdIndex] ?? null,\n adCount: internal.ads.length,\n programmaticPlaying: internal.programmaticPlaying,\n prefetchedReady: internal.prefetchedReady ?? false,\n waterfallMode: internal.waterfallMode,\n screenId: internal.screenId,\n deviceId: internal.deviceId,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\nimport { DEFAULT_PUBLIC_STATE } from '../core/state';\nimport type { TrillboardsWebViewHandle } from './TrillboardsWebView';\n\nexport interface UseTrillboardsNativeReturn {\n ref: React.RefObject<TrillboardsWebViewHandle>;\n isReady: boolean;\n state: TrillboardsState;\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n onReady: () => void;\n onStateChanged: (state: TrillboardsState) => void;\n}\n\nexport function useTrillboardsNative(): UseTrillboardsNativeReturn {\n const ref = useRef<TrillboardsWebViewHandle>(null);\n const [isReady, setIsReady] = useState(false);\n const [state, setState] = useState<TrillboardsState>(DEFAULT_PUBLIC_STATE);\n\n const show = useCallback(() => ref.current?.show(), []);\n const hide = useCallback(() => ref.current?.hide(), []);\n const skipAd = useCallback(() => ref.current?.skipAd(), []);\n const refresh = useCallback(() => ref.current?.refresh(), []);\n const configure = useCallback(\n (params: { waterfall?: WaterfallMode; volume?: number }) => ref.current?.configure(params),\n []\n );\n\n const onReady = useCallback(() => setIsReady(true), []);\n const onStateChanged = useCallback((newState: TrillboardsState) => setState(newState), []);\n\n return {\n ref,\n isReady,\n state,\n show,\n hide,\n skipAd,\n refresh,\n configure,\n onReady,\n onStateChanged,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/react-native/TrillboardsWebView.tsx","../src/core/state.ts","../src/react-native/useTrillboardsNative.ts"],"names":["forwardRef","useRef","useState","useCallback","useImperativeHandle","jsx"],"mappings":"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","file":"react-native.js","sourcesContent":["import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\n\n// Type-only imports to avoid requiring react-native at build time\ntype WebViewType = any;\n\n// Lazy-load react-native-webview once, outside of render\nlet _WebView: any = undefined;\nfunction getWebView(): any {\n if (_WebView === undefined) {\n try {\n _WebView = require('react-native-webview').default;\n } catch {\n _WebView = null;\n }\n }\n return _WebView;\n}\n\ninterface TrillboardsWebViewProps {\n deviceId: string;\n waterfall?: WaterfallMode;\n autoStart?: boolean;\n apiBase?: string;\n style?: any;\n onReady?: () => void;\n onAdStarted?: (data: EventMap['ad_started']) => void;\n onAdEnded?: (data: EventMap['ad_ended']) => void;\n onAdError?: (data: EventMap['ad_error']) => void;\n onProgrammaticStarted?: (data: EventMap['programmatic_started']) => void;\n onProgrammaticEnded?: (data: EventMap['programmatic_ended']) => void;\n onStateChanged?: (state: TrillboardsState) => void;\n}\n\nexport interface TrillboardsWebViewHandle {\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n getState: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n}\n\nconst EMBED_BASE_URL = 'https://screen.trillboards.com/partner-sdk/embed.html';\n\nexport const TrillboardsWebView = forwardRef<TrillboardsWebViewHandle, TrillboardsWebViewProps>(\n (\n {\n deviceId,\n waterfall = 'programmatic_only',\n autoStart = false,\n apiBase,\n style,\n onReady,\n onAdStarted,\n onAdEnded,\n onAdError,\n onProgrammaticStarted,\n onProgrammaticEnded,\n onStateChanged,\n },\n ref\n ) => {\n const webViewRef = useRef<WebViewType>(null);\n const [isReady, setIsReady] = useState(false);\n\n // Store callbacks in refs to prevent unnecessary WebView re-renders\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onAdStartedRef = useRef(onAdStarted);\n onAdStartedRef.current = onAdStarted;\n const onAdEndedRef = useRef(onAdEnded);\n onAdEndedRef.current = onAdEnded;\n const onAdErrorRef = useRef(onAdError);\n onAdErrorRef.current = onAdError;\n const onProgrammaticStartedRef = useRef(onProgrammaticStarted);\n onProgrammaticStartedRef.current = onProgrammaticStarted;\n const onProgrammaticEndedRef = useRef(onProgrammaticEnded);\n onProgrammaticEndedRef.current = onProgrammaticEnded;\n const onStateChangedRef = useRef(onStateChanged);\n onStateChangedRef.current = onStateChanged;\n\n // Build embed URL\n const embedUrl = (() => {\n const params = new URLSearchParams({\n device_id: deviceId,\n waterfall,\n auto_start: autoStart ? '1' : '0',\n });\n if (apiBase) params.set('api_base', apiBase);\n return `${EMBED_BASE_URL}?${params.toString()}`;\n })();\n\n // Send command to WebView — use JSON.stringify for both action and params to prevent XSS\n const sendCommand = useCallback(\n (action: string, params: Record<string, unknown> = {}) => {\n if (!webViewRef.current) return;\n const js = `\n window.postMessage({\n type: 'trillboards-command',\n action: ${JSON.stringify(action)},\n params: ${JSON.stringify(params)}\n }, '*');\n true;\n `;\n webViewRef.current.injectJavaScript(js);\n },\n []\n );\n\n // Expose imperative handle\n useImperativeHandle(\n ref,\n () => ({\n show: () => sendCommand('show'),\n hide: () => sendCommand('hide'),\n skipAd: () => sendCommand('skip'),\n refresh: () => sendCommand('refresh'),\n getState: () => sendCommand('getState'),\n configure: (params) => sendCommand('configure', params),\n }),\n [sendCommand]\n );\n\n // Handle messages from WebView — use refs so dependency array is stable\n const handleMessage = useCallback(\n (event: any) => {\n try {\n const data = typeof event.nativeEvent.data === 'string'\n ? JSON.parse(event.nativeEvent.data)\n : event.nativeEvent.data;\n\n if (data.type !== 'trillboards') return;\n\n switch (data.event) {\n case 'initialized':\n setIsReady(true);\n onReadyRef.current?.();\n break;\n case 'ad_started':\n onAdStartedRef.current?.(data.data);\n break;\n case 'ad_ended':\n onAdEndedRef.current?.(data.data);\n break;\n case 'ad_error':\n onAdErrorRef.current?.(data.data);\n break;\n case 'programmatic_started':\n onProgrammaticStartedRef.current?.(data.data);\n break;\n case 'programmatic_ended':\n onProgrammaticEndedRef.current?.(data.data);\n break;\n case 'state_changed':\n onStateChangedRef.current?.(data.data);\n break;\n }\n } catch {\n // Ignore non-JSON messages\n }\n },\n []\n );\n\n const WebView = getWebView();\n if (!WebView) return null;\n\n return (\n <WebView\n ref={webViewRef}\n source={{ uri: embedUrl }}\n style={[{ flex: 1, backgroundColor: '#000' }, style]}\n onMessage={handleMessage}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n mediaPlaybackRequiresUserAction={false}\n allowsInlineMediaPlayback={true}\n mixedContentMode=\"compatibility\"\n originWhitelist={['https://*', 'http://*']}\n />\n );\n }\n);\n\nTrillboardsWebView.displayName = 'TrillboardsWebView';\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Internal + public state management\n// ─────────────────────────────────────────────────────────────\n\nimport type {\n AdItem,\n TrillboardsState,\n WaterfallMode,\n ProgrammaticSettings,\n ScreenDimensions,\n AdDeliveryProfile,\n} from './types';\n\n/**\n * The full internal state held by the SDK engine.\n * This is NOT exposed to consumers — they receive the\n * trimmed-down `TrillboardsState` via `getPublicState()`.\n */\nexport interface InternalState {\n deviceId: string | null;\n partnerId: string | null;\n screenId: string | null;\n ads: AdItem[];\n currentAdIndex: number;\n isPlaying: boolean;\n isPaused: boolean;\n isOffline: boolean;\n settings: Record<string, unknown>;\n programmatic: ProgrammaticSettings | null;\n programmaticPlaying: boolean;\n prefetchedReady: boolean;\n waterfallMode: WaterfallMode;\n autoStart: boolean;\n container: HTMLElement | null;\n adTimer: ReturnType<typeof setTimeout> | null;\n refreshTimer: ReturnType<typeof setInterval> | null;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n programmaticRetryTimer: ReturnType<typeof setTimeout> | null;\n programmaticRetryActive: boolean;\n programmaticRetryCount: number;\n programmaticLastError: string | null;\n adDeliveryProfile: AdDeliveryProfile | null;\n initialized: boolean;\n screenOrientation: string | null;\n screenDimensions: ScreenDimensions | null;\n etag: string | null;\n}\n\n/**\n * Default public state used by React components and the IIFE\n * entry point before the SDK is initialized.\n */\nexport const DEFAULT_PUBLIC_STATE: TrillboardsState = {\n initialized: false,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n currentAd: null,\n adCount: 0,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n adDeliveryProfileMode: null,\n screenId: null,\n deviceId: null,\n};\n\n/**\n * Create a fresh internal state object with safe defaults.\n * Called once during SDK initialisation.\n */\nexport function createInitialState(): InternalState {\n return {\n deviceId: null,\n partnerId: null,\n screenId: null,\n ads: [],\n currentAdIndex: 0,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n settings: {},\n programmatic: null,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n autoStart: true,\n container: null,\n adTimer: null,\n refreshTimer: null,\n heartbeatTimer: null,\n programmaticRetryTimer: null,\n programmaticRetryActive: false,\n programmaticRetryCount: 0,\n programmaticLastError: null,\n adDeliveryProfile: null,\n initialized: false,\n screenOrientation: null,\n screenDimensions: null,\n etag: null,\n };\n}\n\n/**\n * Project the internal state into a safe, read-only snapshot\n * that can be handed to consumers and event listeners.\n */\nexport function getPublicState(internal: InternalState): TrillboardsState {\n return {\n initialized: internal.initialized,\n isPlaying: internal.isPlaying,\n isPaused: internal.isPaused,\n isOffline: internal.isOffline,\n currentAd: internal.ads[internal.currentAdIndex] ?? null,\n adCount: internal.ads.length,\n programmaticPlaying: internal.programmaticPlaying,\n prefetchedReady: internal.prefetchedReady ?? false,\n waterfallMode: internal.waterfallMode,\n adDeliveryProfileMode: internal.adDeliveryProfile?.mode ?? null,\n screenId: internal.screenId,\n deviceId: internal.deviceId,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\nimport { DEFAULT_PUBLIC_STATE } from '../core/state';\nimport type { TrillboardsWebViewHandle } from './TrillboardsWebView';\n\nexport interface UseTrillboardsNativeReturn {\n ref: React.RefObject<TrillboardsWebViewHandle>;\n isReady: boolean;\n state: TrillboardsState;\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n onReady: () => void;\n onStateChanged: (state: TrillboardsState) => void;\n}\n\nexport function useTrillboardsNative(): UseTrillboardsNativeReturn {\n const ref = useRef<TrillboardsWebViewHandle>(null);\n const [isReady, setIsReady] = useState(false);\n const [state, setState] = useState<TrillboardsState>(DEFAULT_PUBLIC_STATE);\n\n const show = useCallback(() => ref.current?.show(), []);\n const hide = useCallback(() => ref.current?.hide(), []);\n const skipAd = useCallback(() => ref.current?.skipAd(), []);\n const refresh = useCallback(() => ref.current?.refresh(), []);\n const configure = useCallback(\n (params: { waterfall?: WaterfallMode; volume?: number }) => ref.current?.configure(params),\n []\n );\n\n const onReady = useCallback(() => setIsReady(true), []);\n const onStateChanged = useCallback((newState: TrillboardsState) => setState(newState), []);\n\n return {\n ref,\n isReady,\n state,\n show,\n hide,\n skipAd,\n refresh,\n configure,\n onReady,\n onStateChanged,\n };\n}\n"]}
@@ -152,6 +152,7 @@ var DEFAULT_PUBLIC_STATE = {
152
152
  programmaticPlaying: false,
153
153
  prefetchedReady: false,
154
154
  waterfallMode: "programmatic_only",
155
+ adDeliveryProfileMode: null,
155
156
  screenId: null,
156
157
  deviceId: null
157
158
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-native/TrillboardsWebView.tsx","../src/core/state.ts","../src/react-native/useTrillboardsNative.ts"],"names":["useRef","useState","useCallback"],"mappings":"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","file":"react-native.mjs","sourcesContent":["import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\n\n// Type-only imports to avoid requiring react-native at build time\ntype WebViewType = any;\n\n// Lazy-load react-native-webview once, outside of render\nlet _WebView: any = undefined;\nfunction getWebView(): any {\n if (_WebView === undefined) {\n try {\n _WebView = require('react-native-webview').default;\n } catch {\n _WebView = null;\n }\n }\n return _WebView;\n}\n\ninterface TrillboardsWebViewProps {\n deviceId: string;\n waterfall?: WaterfallMode;\n autoStart?: boolean;\n apiBase?: string;\n style?: any;\n onReady?: () => void;\n onAdStarted?: (data: EventMap['ad_started']) => void;\n onAdEnded?: (data: EventMap['ad_ended']) => void;\n onAdError?: (data: EventMap['ad_error']) => void;\n onProgrammaticStarted?: (data: EventMap['programmatic_started']) => void;\n onProgrammaticEnded?: (data: EventMap['programmatic_ended']) => void;\n onStateChanged?: (state: TrillboardsState) => void;\n}\n\nexport interface TrillboardsWebViewHandle {\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n getState: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n}\n\nconst EMBED_BASE_URL = 'https://screen.trillboards.com/partner-sdk/embed.html';\n\nexport const TrillboardsWebView = forwardRef<TrillboardsWebViewHandle, TrillboardsWebViewProps>(\n (\n {\n deviceId,\n waterfall = 'programmatic_only',\n autoStart = false,\n apiBase,\n style,\n onReady,\n onAdStarted,\n onAdEnded,\n onAdError,\n onProgrammaticStarted,\n onProgrammaticEnded,\n onStateChanged,\n },\n ref\n ) => {\n const webViewRef = useRef<WebViewType>(null);\n const [isReady, setIsReady] = useState(false);\n\n // Store callbacks in refs to prevent unnecessary WebView re-renders\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onAdStartedRef = useRef(onAdStarted);\n onAdStartedRef.current = onAdStarted;\n const onAdEndedRef = useRef(onAdEnded);\n onAdEndedRef.current = onAdEnded;\n const onAdErrorRef = useRef(onAdError);\n onAdErrorRef.current = onAdError;\n const onProgrammaticStartedRef = useRef(onProgrammaticStarted);\n onProgrammaticStartedRef.current = onProgrammaticStarted;\n const onProgrammaticEndedRef = useRef(onProgrammaticEnded);\n onProgrammaticEndedRef.current = onProgrammaticEnded;\n const onStateChangedRef = useRef(onStateChanged);\n onStateChangedRef.current = onStateChanged;\n\n // Build embed URL\n const embedUrl = (() => {\n const params = new URLSearchParams({\n device_id: deviceId,\n waterfall,\n auto_start: autoStart ? '1' : '0',\n });\n if (apiBase) params.set('api_base', apiBase);\n return `${EMBED_BASE_URL}?${params.toString()}`;\n })();\n\n // Send command to WebView — use JSON.stringify for both action and params to prevent XSS\n const sendCommand = useCallback(\n (action: string, params: Record<string, unknown> = {}) => {\n if (!webViewRef.current) return;\n const js = `\n window.postMessage({\n type: 'trillboards-command',\n action: ${JSON.stringify(action)},\n params: ${JSON.stringify(params)}\n }, '*');\n true;\n `;\n webViewRef.current.injectJavaScript(js);\n },\n []\n );\n\n // Expose imperative handle\n useImperativeHandle(\n ref,\n () => ({\n show: () => sendCommand('show'),\n hide: () => sendCommand('hide'),\n skipAd: () => sendCommand('skip'),\n refresh: () => sendCommand('refresh'),\n getState: () => sendCommand('getState'),\n configure: (params) => sendCommand('configure', params),\n }),\n [sendCommand]\n );\n\n // Handle messages from WebView — use refs so dependency array is stable\n const handleMessage = useCallback(\n (event: any) => {\n try {\n const data = typeof event.nativeEvent.data === 'string'\n ? JSON.parse(event.nativeEvent.data)\n : event.nativeEvent.data;\n\n if (data.type !== 'trillboards') return;\n\n switch (data.event) {\n case 'initialized':\n setIsReady(true);\n onReadyRef.current?.();\n break;\n case 'ad_started':\n onAdStartedRef.current?.(data.data);\n break;\n case 'ad_ended':\n onAdEndedRef.current?.(data.data);\n break;\n case 'ad_error':\n onAdErrorRef.current?.(data.data);\n break;\n case 'programmatic_started':\n onProgrammaticStartedRef.current?.(data.data);\n break;\n case 'programmatic_ended':\n onProgrammaticEndedRef.current?.(data.data);\n break;\n case 'state_changed':\n onStateChangedRef.current?.(data.data);\n break;\n }\n } catch {\n // Ignore non-JSON messages\n }\n },\n []\n );\n\n const WebView = getWebView();\n if (!WebView) return null;\n\n return (\n <WebView\n ref={webViewRef}\n source={{ uri: embedUrl }}\n style={[{ flex: 1, backgroundColor: '#000' }, style]}\n onMessage={handleMessage}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n mediaPlaybackRequiresUserAction={false}\n allowsInlineMediaPlayback={true}\n mixedContentMode=\"compatibility\"\n originWhitelist={['https://*', 'http://*']}\n />\n );\n }\n);\n\nTrillboardsWebView.displayName = 'TrillboardsWebView';\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Internal + public state management\n// ─────────────────────────────────────────────────────────────\n\nimport type {\n AdItem,\n TrillboardsState,\n WaterfallMode,\n ProgrammaticSettings,\n ScreenDimensions,\n} from './types';\n\n/**\n * The full internal state held by the SDK engine.\n * This is NOT exposed to consumers — they receive the\n * trimmed-down `TrillboardsState` via `getPublicState()`.\n */\nexport interface InternalState {\n deviceId: string | null;\n partnerId: string | null;\n screenId: string | null;\n ads: AdItem[];\n currentAdIndex: number;\n isPlaying: boolean;\n isPaused: boolean;\n isOffline: boolean;\n settings: Record<string, unknown>;\n programmatic: ProgrammaticSettings | null;\n programmaticPlaying: boolean;\n prefetchedReady: boolean;\n waterfallMode: WaterfallMode;\n autoStart: boolean;\n container: HTMLElement | null;\n adTimer: ReturnType<typeof setTimeout> | null;\n refreshTimer: ReturnType<typeof setInterval> | null;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n programmaticRetryTimer: ReturnType<typeof setTimeout> | null;\n programmaticRetryActive: boolean;\n programmaticRetryCount: number;\n programmaticLastError: string | null;\n initialized: boolean;\n screenOrientation: string | null;\n screenDimensions: ScreenDimensions | null;\n etag: string | null;\n}\n\n/**\n * Default public state used by React components and the IIFE\n * entry point before the SDK is initialized.\n */\nexport const DEFAULT_PUBLIC_STATE: TrillboardsState = {\n initialized: false,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n currentAd: null,\n adCount: 0,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n screenId: null,\n deviceId: null,\n};\n\n/**\n * Create a fresh internal state object with safe defaults.\n * Called once during SDK initialisation.\n */\nexport function createInitialState(): InternalState {\n return {\n deviceId: null,\n partnerId: null,\n screenId: null,\n ads: [],\n currentAdIndex: 0,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n settings: {},\n programmatic: null,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n autoStart: true,\n container: null,\n adTimer: null,\n refreshTimer: null,\n heartbeatTimer: null,\n programmaticRetryTimer: null,\n programmaticRetryActive: false,\n programmaticRetryCount: 0,\n programmaticLastError: null,\n initialized: false,\n screenOrientation: null,\n screenDimensions: null,\n etag: null,\n };\n}\n\n/**\n * Project the internal state into a safe, read-only snapshot\n * that can be handed to consumers and event listeners.\n */\nexport function getPublicState(internal: InternalState): TrillboardsState {\n return {\n initialized: internal.initialized,\n isPlaying: internal.isPlaying,\n isPaused: internal.isPaused,\n isOffline: internal.isOffline,\n currentAd: internal.ads[internal.currentAdIndex] ?? null,\n adCount: internal.ads.length,\n programmaticPlaying: internal.programmaticPlaying,\n prefetchedReady: internal.prefetchedReady ?? false,\n waterfallMode: internal.waterfallMode,\n screenId: internal.screenId,\n deviceId: internal.deviceId,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\nimport { DEFAULT_PUBLIC_STATE } from '../core/state';\nimport type { TrillboardsWebViewHandle } from './TrillboardsWebView';\n\nexport interface UseTrillboardsNativeReturn {\n ref: React.RefObject<TrillboardsWebViewHandle>;\n isReady: boolean;\n state: TrillboardsState;\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n onReady: () => void;\n onStateChanged: (state: TrillboardsState) => void;\n}\n\nexport function useTrillboardsNative(): UseTrillboardsNativeReturn {\n const ref = useRef<TrillboardsWebViewHandle>(null);\n const [isReady, setIsReady] = useState(false);\n const [state, setState] = useState<TrillboardsState>(DEFAULT_PUBLIC_STATE);\n\n const show = useCallback(() => ref.current?.show(), []);\n const hide = useCallback(() => ref.current?.hide(), []);\n const skipAd = useCallback(() => ref.current?.skipAd(), []);\n const refresh = useCallback(() => ref.current?.refresh(), []);\n const configure = useCallback(\n (params: { waterfall?: WaterfallMode; volume?: number }) => ref.current?.configure(params),\n []\n );\n\n const onReady = useCallback(() => setIsReady(true), []);\n const onStateChanged = useCallback((newState: TrillboardsState) => setState(newState), []);\n\n return {\n ref,\n isReady,\n state,\n show,\n hide,\n skipAd,\n refresh,\n configure,\n onReady,\n onStateChanged,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/react-native/TrillboardsWebView.tsx","../src/core/state.ts","../src/react-native/useTrillboardsNative.ts"],"names":["useRef","useState","useCallback"],"mappings":";;;;;;;;;AAOA,IAAI,QAAA,GAAgB,MAAA;AACpB,SAAS,UAAA,GAAkB;AACzB,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,SAAA,CAAQ,sBAAsB,CAAA,CAAE,OAAA;AAAA,IAC7C,CAAA,CAAA,MAAQ;AACN,MAAA,QAAA,GAAW,IAAA;AAAA,IACb;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AA0BA,IAAM,cAAA,GAAiB,uDAAA;AAEhB,IAAM,kBAAA,GAAqB,UAAA;AAAA,EAChC,CACE;AAAA,IACE,QAAA;AAAA,IACA,SAAA,GAAY,mBAAA;AAAA,IACZ,SAAA,GAAY,KAAA;AAAA,IACZ,OAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA,qBAAA;AAAA,IACA,mBAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,MAAM,UAAA,GAAa,OAAoB,IAAI,CAAA;AAC3C,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAG5C,IAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,IAAA,MAAM,cAAA,GAAiB,OAAO,WAAW,CAAA;AACzC,IAAA,cAAA,CAAe,OAAA,GAAU,WAAA;AACzB,IAAA,MAAM,YAAA,GAAe,OAAO,SAAS,CAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AACvB,IAAA,MAAM,YAAA,GAAe,OAAO,SAAS,CAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AACvB,IAAA,MAAM,wBAAA,GAA2B,OAAO,qBAAqB,CAAA;AAC7D,IAAA,wBAAA,CAAyB,OAAA,GAAU,qBAAA;AACnC,IAAA,MAAM,sBAAA,GAAyB,OAAO,mBAAmB,CAAA;AACzD,IAAA,sBAAA,CAAuB,OAAA,GAAU,mBAAA;AACjC,IAAA,MAAM,iBAAA,GAAoB,OAAO,cAAc,CAAA;AAC/C,IAAA,iBAAA,CAAkB,OAAA,GAAU,cAAA;AAG5B,IAAA,MAAM,YAAY,MAAM;AACtB,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB;AAAA,QACjC,SAAA,EAAW,QAAA;AAAA,QACX,SAAA;AAAA,QACA,UAAA,EAAY,YAAY,GAAA,GAAM;AAAA,OAC/B,CAAA;AACD,MAAA,IAAI,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,UAAA,EAAY,OAAO,CAAA;AAC3C,MAAA,OAAO,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,MAAA,CAAO,UAAU,CAAA,CAAA;AAAA,IAC/C,CAAA,GAAG;AAGH,IAAA,MAAM,WAAA,GAAc,WAAA;AAAA,MAClB,CAAC,MAAA,EAAgB,MAAA,GAAkC,EAAC,KAAM;AACxD,QAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACzB,QAAA,MAAM,EAAA,GAAK;AAAA;AAAA;AAAA,oBAAA,EAGG,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,oBAAA,EACtB,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC;AAAA;AAAA;AAAA,QAAA,CAAA;AAIpC,QAAA,UAAA,CAAW,OAAA,CAAQ,iBAAiB,EAAE,CAAA;AAAA,MACxC,CAAA;AAAA,MACA;AAAC,KACH;AAGA,IAAA,mBAAA;AAAA,MACE,GAAA;AAAA,MACA,OAAO;AAAA,QACL,IAAA,EAAM,MAAM,WAAA,CAAY,MAAM,CAAA;AAAA,QAC9B,IAAA,EAAM,MAAM,WAAA,CAAY,MAAM,CAAA;AAAA,QAC9B,MAAA,EAAQ,MAAM,WAAA,CAAY,MAAM,CAAA;AAAA,QAChC,OAAA,EAAS,MAAM,WAAA,CAAY,SAAS,CAAA;AAAA,QACpC,QAAA,EAAU,MAAM,WAAA,CAAY,UAAU,CAAA;AAAA,QACtC,SAAA,EAAW,CAAC,MAAA,KAAW,WAAA,CAAY,aAAa,MAAM;AAAA,OACxD,CAAA;AAAA,MACA,CAAC,WAAW;AAAA,KACd;AAGA,IAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,MACpB,CAAC,KAAA,KAAe;AACd,QAAA,IAAI;AACF,UAAA,MAAM,IAAA,GAAO,OAAO,KAAA,CAAM,WAAA,CAAY,IAAA,KAAS,QAAA,GAC3C,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,WAAA,CAAY,IAAI,CAAA,GACjC,MAAM,WAAA,CAAY,IAAA;AAEtB,UAAA,IAAI,IAAA,CAAK,SAAS,aAAA,EAAe;AAEjC,UAAA,QAAQ,KAAK,KAAA;AAAO,YAClB,KAAK,aAAA;AACH,cAAA,UAAA,CAAW,IAAI,CAAA;AACf,cAAA,UAAA,CAAW,OAAA,IAAU;AACrB,cAAA;AAAA,YACF,KAAK,YAAA;AACH,cAAA,cAAA,CAAe,OAAA,GAAU,KAAK,IAAI,CAAA;AAClC,cAAA;AAAA,YACF,KAAK,UAAA;AACH,cAAA,YAAA,CAAa,OAAA,GAAU,KAAK,IAAI,CAAA;AAChC,cAAA;AAAA,YACF,KAAK,UAAA;AACH,cAAA,YAAA,CAAa,OAAA,GAAU,KAAK,IAAI,CAAA;AAChC,cAAA;AAAA,YACF,KAAK,sBAAA;AACH,cAAA,wBAAA,CAAyB,OAAA,GAAU,KAAK,IAAI,CAAA;AAC5C,cAAA;AAAA,YACF,KAAK,oBAAA;AACH,cAAA,sBAAA,CAAuB,OAAA,GAAU,KAAK,IAAI,CAAA;AAC1C,cAAA;AAAA,YACF,KAAK,eAAA;AACH,cAAA,iBAAA,CAAkB,OAAA,GAAU,KAAK,IAAI,CAAA;AACrC,cAAA;AAAA;AACJ,QACF,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF,CAAA;AAAA,MACA;AAAC,KACH;AAEA,IAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,IAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,IAAA,uBACE,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,UAAA;AAAA,QACL,MAAA,EAAQ,EAAE,GAAA,EAAK,QAAA,EAAS;AAAA,QACxB,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,GAAG,eAAA,EAAiB,MAAA,IAAU,KAAK,CAAA;AAAA,QACnD,SAAA,EAAW,aAAA;AAAA,QACX,iBAAA,EAAmB,IAAA;AAAA,QACnB,iBAAA,EAAmB,IAAA;AAAA,QACnB,+BAAA,EAAiC,KAAA;AAAA,QACjC,yBAAA,EAA2B,IAAA;AAAA,QAC3B,gBAAA,EAAiB,eAAA;AAAA,QACjB,eAAA,EAAiB,CAAC,WAAA,EAAa,UAAU;AAAA;AAAA,KAC3C;AAAA,EAEJ;AACF;AAEA,kBAAA,CAAmB,WAAA,GAAc,oBAAA;;;ACrI1B,IAAM,oBAAA,GAAyC;AAAA,EACpD,WAAA,EAAa,KAAA;AAAA,EACb,SAAA,EAAW,KAAA;AAAA,EACX,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,KAAA;AAAA,EACX,SAAA,EAAW,IAAA;AAAA,EACX,OAAA,EAAS,CAAA;AAAA,EACT,mBAAA,EAAqB,KAAA;AAAA,EACrB,eAAA,EAAiB,KAAA;AAAA,EACjB,aAAA,EAAe,mBAAA;AAAA,EACf,qBAAA,EAAuB,IAAA;AAAA,EACvB,QAAA,EAAU,IAAA;AAAA,EACV,QAAA,EAAU;AACZ,CAAA;;;AC/CO,SAAS,oBAAA,GAAmD;AACjE,EAAA,MAAM,GAAA,GAAMA,OAAiC,IAAI,CAAA;AACjD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAA2B,oBAAoB,CAAA;AAEzE,EAAA,MAAM,IAAA,GAAOC,YAAY,MAAM,GAAA,CAAI,SAAS,IAAA,EAAK,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,IAAA,GAAOA,YAAY,MAAM,GAAA,CAAI,SAAS,IAAA,EAAK,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,MAAA,GAASA,YAAY,MAAM,GAAA,CAAI,SAAS,MAAA,EAAO,EAAG,EAAE,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAUA,YAAY,MAAM,GAAA,CAAI,SAAS,OAAA,EAAQ,EAAG,EAAE,CAAA;AAC5D,EAAA,MAAM,SAAA,GAAYA,WAAAA;AAAA,IAChB,CAAC,MAAA,KAA2D,GAAA,CAAI,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,IACzF;AAAC,GACH;AAEA,EAAA,MAAM,UAAUA,WAAAA,CAAY,MAAM,WAAW,IAAI,CAAA,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,cAAA,GAAiBA,YAAY,CAAC,QAAA,KAA+B,SAAS,QAAQ,CAAA,EAAG,EAAE,CAAA;AAEzF,EAAA,OAAO;AAAA,IACL,GAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF","file":"react-native.mjs","sourcesContent":["import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\n\n// Type-only imports to avoid requiring react-native at build time\ntype WebViewType = any;\n\n// Lazy-load react-native-webview once, outside of render\nlet _WebView: any = undefined;\nfunction getWebView(): any {\n if (_WebView === undefined) {\n try {\n _WebView = require('react-native-webview').default;\n } catch {\n _WebView = null;\n }\n }\n return _WebView;\n}\n\ninterface TrillboardsWebViewProps {\n deviceId: string;\n waterfall?: WaterfallMode;\n autoStart?: boolean;\n apiBase?: string;\n style?: any;\n onReady?: () => void;\n onAdStarted?: (data: EventMap['ad_started']) => void;\n onAdEnded?: (data: EventMap['ad_ended']) => void;\n onAdError?: (data: EventMap['ad_error']) => void;\n onProgrammaticStarted?: (data: EventMap['programmatic_started']) => void;\n onProgrammaticEnded?: (data: EventMap['programmatic_ended']) => void;\n onStateChanged?: (state: TrillboardsState) => void;\n}\n\nexport interface TrillboardsWebViewHandle {\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n getState: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n}\n\nconst EMBED_BASE_URL = 'https://screen.trillboards.com/partner-sdk/embed.html';\n\nexport const TrillboardsWebView = forwardRef<TrillboardsWebViewHandle, TrillboardsWebViewProps>(\n (\n {\n deviceId,\n waterfall = 'programmatic_only',\n autoStart = false,\n apiBase,\n style,\n onReady,\n onAdStarted,\n onAdEnded,\n onAdError,\n onProgrammaticStarted,\n onProgrammaticEnded,\n onStateChanged,\n },\n ref\n ) => {\n const webViewRef = useRef<WebViewType>(null);\n const [isReady, setIsReady] = useState(false);\n\n // Store callbacks in refs to prevent unnecessary WebView re-renders\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onAdStartedRef = useRef(onAdStarted);\n onAdStartedRef.current = onAdStarted;\n const onAdEndedRef = useRef(onAdEnded);\n onAdEndedRef.current = onAdEnded;\n const onAdErrorRef = useRef(onAdError);\n onAdErrorRef.current = onAdError;\n const onProgrammaticStartedRef = useRef(onProgrammaticStarted);\n onProgrammaticStartedRef.current = onProgrammaticStarted;\n const onProgrammaticEndedRef = useRef(onProgrammaticEnded);\n onProgrammaticEndedRef.current = onProgrammaticEnded;\n const onStateChangedRef = useRef(onStateChanged);\n onStateChangedRef.current = onStateChanged;\n\n // Build embed URL\n const embedUrl = (() => {\n const params = new URLSearchParams({\n device_id: deviceId,\n waterfall,\n auto_start: autoStart ? '1' : '0',\n });\n if (apiBase) params.set('api_base', apiBase);\n return `${EMBED_BASE_URL}?${params.toString()}`;\n })();\n\n // Send command to WebView — use JSON.stringify for both action and params to prevent XSS\n const sendCommand = useCallback(\n (action: string, params: Record<string, unknown> = {}) => {\n if (!webViewRef.current) return;\n const js = `\n window.postMessage({\n type: 'trillboards-command',\n action: ${JSON.stringify(action)},\n params: ${JSON.stringify(params)}\n }, '*');\n true;\n `;\n webViewRef.current.injectJavaScript(js);\n },\n []\n );\n\n // Expose imperative handle\n useImperativeHandle(\n ref,\n () => ({\n show: () => sendCommand('show'),\n hide: () => sendCommand('hide'),\n skipAd: () => sendCommand('skip'),\n refresh: () => sendCommand('refresh'),\n getState: () => sendCommand('getState'),\n configure: (params) => sendCommand('configure', params),\n }),\n [sendCommand]\n );\n\n // Handle messages from WebView — use refs so dependency array is stable\n const handleMessage = useCallback(\n (event: any) => {\n try {\n const data = typeof event.nativeEvent.data === 'string'\n ? JSON.parse(event.nativeEvent.data)\n : event.nativeEvent.data;\n\n if (data.type !== 'trillboards') return;\n\n switch (data.event) {\n case 'initialized':\n setIsReady(true);\n onReadyRef.current?.();\n break;\n case 'ad_started':\n onAdStartedRef.current?.(data.data);\n break;\n case 'ad_ended':\n onAdEndedRef.current?.(data.data);\n break;\n case 'ad_error':\n onAdErrorRef.current?.(data.data);\n break;\n case 'programmatic_started':\n onProgrammaticStartedRef.current?.(data.data);\n break;\n case 'programmatic_ended':\n onProgrammaticEndedRef.current?.(data.data);\n break;\n case 'state_changed':\n onStateChangedRef.current?.(data.data);\n break;\n }\n } catch {\n // Ignore non-JSON messages\n }\n },\n []\n );\n\n const WebView = getWebView();\n if (!WebView) return null;\n\n return (\n <WebView\n ref={webViewRef}\n source={{ uri: embedUrl }}\n style={[{ flex: 1, backgroundColor: '#000' }, style]}\n onMessage={handleMessage}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n mediaPlaybackRequiresUserAction={false}\n allowsInlineMediaPlayback={true}\n mixedContentMode=\"compatibility\"\n originWhitelist={['https://*', 'http://*']}\n />\n );\n }\n);\n\nTrillboardsWebView.displayName = 'TrillboardsWebView';\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Internal + public state management\n// ─────────────────────────────────────────────────────────────\n\nimport type {\n AdItem,\n TrillboardsState,\n WaterfallMode,\n ProgrammaticSettings,\n ScreenDimensions,\n AdDeliveryProfile,\n} from './types';\n\n/**\n * The full internal state held by the SDK engine.\n * This is NOT exposed to consumers — they receive the\n * trimmed-down `TrillboardsState` via `getPublicState()`.\n */\nexport interface InternalState {\n deviceId: string | null;\n partnerId: string | null;\n screenId: string | null;\n ads: AdItem[];\n currentAdIndex: number;\n isPlaying: boolean;\n isPaused: boolean;\n isOffline: boolean;\n settings: Record<string, unknown>;\n programmatic: ProgrammaticSettings | null;\n programmaticPlaying: boolean;\n prefetchedReady: boolean;\n waterfallMode: WaterfallMode;\n autoStart: boolean;\n container: HTMLElement | null;\n adTimer: ReturnType<typeof setTimeout> | null;\n refreshTimer: ReturnType<typeof setInterval> | null;\n heartbeatTimer: ReturnType<typeof setInterval> | null;\n programmaticRetryTimer: ReturnType<typeof setTimeout> | null;\n programmaticRetryActive: boolean;\n programmaticRetryCount: number;\n programmaticLastError: string | null;\n adDeliveryProfile: AdDeliveryProfile | null;\n initialized: boolean;\n screenOrientation: string | null;\n screenDimensions: ScreenDimensions | null;\n etag: string | null;\n}\n\n/**\n * Default public state used by React components and the IIFE\n * entry point before the SDK is initialized.\n */\nexport const DEFAULT_PUBLIC_STATE: TrillboardsState = {\n initialized: false,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n currentAd: null,\n adCount: 0,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n adDeliveryProfileMode: null,\n screenId: null,\n deviceId: null,\n};\n\n/**\n * Create a fresh internal state object with safe defaults.\n * Called once during SDK initialisation.\n */\nexport function createInitialState(): InternalState {\n return {\n deviceId: null,\n partnerId: null,\n screenId: null,\n ads: [],\n currentAdIndex: 0,\n isPlaying: false,\n isPaused: false,\n isOffline: false,\n settings: {},\n programmatic: null,\n programmaticPlaying: false,\n prefetchedReady: false,\n waterfallMode: 'programmatic_only',\n autoStart: true,\n container: null,\n adTimer: null,\n refreshTimer: null,\n heartbeatTimer: null,\n programmaticRetryTimer: null,\n programmaticRetryActive: false,\n programmaticRetryCount: 0,\n programmaticLastError: null,\n adDeliveryProfile: null,\n initialized: false,\n screenOrientation: null,\n screenDimensions: null,\n etag: null,\n };\n}\n\n/**\n * Project the internal state into a safe, read-only snapshot\n * that can be handed to consumers and event listeners.\n */\nexport function getPublicState(internal: InternalState): TrillboardsState {\n return {\n initialized: internal.initialized,\n isPlaying: internal.isPlaying,\n isPaused: internal.isPaused,\n isOffline: internal.isOffline,\n currentAd: internal.ads[internal.currentAdIndex] ?? null,\n adCount: internal.ads.length,\n programmaticPlaying: internal.programmaticPlaying,\n prefetchedReady: internal.prefetchedReady ?? false,\n waterfallMode: internal.waterfallMode,\n adDeliveryProfileMode: internal.adDeliveryProfile?.mode ?? null,\n screenId: internal.screenId,\n deviceId: internal.deviceId,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { TrillboardsState, EventMap, WaterfallMode } from '../core/types';\nimport { DEFAULT_PUBLIC_STATE } from '../core/state';\nimport type { TrillboardsWebViewHandle } from './TrillboardsWebView';\n\nexport interface UseTrillboardsNativeReturn {\n ref: React.RefObject<TrillboardsWebViewHandle>;\n isReady: boolean;\n state: TrillboardsState;\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => void;\n configure: (params: { waterfall?: WaterfallMode; volume?: number }) => void;\n onReady: () => void;\n onStateChanged: (state: TrillboardsState) => void;\n}\n\nexport function useTrillboardsNative(): UseTrillboardsNativeReturn {\n const ref = useRef<TrillboardsWebViewHandle>(null);\n const [isReady, setIsReady] = useState(false);\n const [state, setState] = useState<TrillboardsState>(DEFAULT_PUBLIC_STATE);\n\n const show = useCallback(() => ref.current?.show(), []);\n const hide = useCallback(() => ref.current?.hide(), []);\n const skipAd = useCallback(() => ref.current?.skipAd(), []);\n const refresh = useCallback(() => ref.current?.refresh(), []);\n const configure = useCallback(\n (params: { waterfall?: WaterfallMode; volume?: number }) => ref.current?.configure(params),\n []\n );\n\n const onReady = useCallback(() => setIsReady(true), []);\n const onStateChanged = useCallback((newState: TrillboardsState) => setState(newState), []);\n\n return {\n ref,\n isReady,\n state,\n show,\n hide,\n skipAd,\n refresh,\n configure,\n onReady,\n onStateChanged,\n };\n}\n"]}
package/dist/react.d.mts CHANGED
@@ -43,6 +43,7 @@ interface TrillboardsState {
43
43
  programmaticPlaying: boolean;
44
44
  prefetchedReady: boolean;
45
45
  waterfallMode: WaterfallMode;
46
+ adDeliveryProfileMode: AdDeliveryMode | null;
46
47
  screenId: string | null;
47
48
  deviceId: string | null;
48
49
  }
@@ -52,6 +53,7 @@ interface BridgeConfig {
52
53
  events?: string[];
53
54
  name?: string;
54
55
  }
56
+ type AdDeliveryMode = 'ima_sdk' | 'vast_fallback';
55
57
  interface EventMap {
56
58
  initialized: {
57
59
  deviceId: string;
@@ -189,6 +191,10 @@ declare class TrillboardsAds {
189
191
  private refreshAds;
190
192
  private startRefreshTimer;
191
193
  private startHeartbeatTimer;
194
+ private sendHeartbeatTick;
195
+ private buildHeartbeatPayload;
196
+ private applyAdDeliveryProfile;
197
+ private runHeartbeatCommands;
192
198
  private playNextAd;
193
199
  private playProgrammatic;
194
200
  private playDirect;
package/dist/react.d.ts CHANGED
@@ -43,6 +43,7 @@ interface TrillboardsState {
43
43
  programmaticPlaying: boolean;
44
44
  prefetchedReady: boolean;
45
45
  waterfallMode: WaterfallMode;
46
+ adDeliveryProfileMode: AdDeliveryMode | null;
46
47
  screenId: string | null;
47
48
  deviceId: string | null;
48
49
  }
@@ -52,6 +53,7 @@ interface BridgeConfig {
52
53
  events?: string[];
53
54
  name?: string;
54
55
  }
56
+ type AdDeliveryMode = 'ima_sdk' | 'vast_fallback';
55
57
  interface EventMap {
56
58
  initialized: {
57
59
  deviceId: string;
@@ -189,6 +191,10 @@ declare class TrillboardsAds {
189
191
  private refreshAds;
190
192
  private startRefreshTimer;
191
193
  private startHeartbeatTimer;
194
+ private sendHeartbeatTick;
195
+ private buildHeartbeatPayload;
196
+ private applyAdDeliveryProfile;
197
+ private runHeartbeatCommands;
192
198
  private playNextAd;
193
199
  private playProgrammatic;
194
200
  private playDirect;
package/dist/react.js CHANGED
@@ -4,6 +4,9 @@ var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
 
6
6
  // src/react/TrillboardsProvider.tsx
7
+
8
+ // src/core/config.ts
9
+ var SDK_VERSION = "2.2.0";
7
10
  var DEFAULT_CONFIG = {
8
11
  API_BASE: "https://api.trillboards.com/v1/partner",
9
12
  CDN_BASE: "https://cdn.trillboards.com",
@@ -58,6 +61,7 @@ var DEFAULT_PUBLIC_STATE = {
58
61
  programmaticPlaying: false,
59
62
  prefetchedReady: false,
60
63
  waterfallMode: "programmatic_only",
64
+ adDeliveryProfileMode: null,
61
65
  screenId: null,
62
66
  deviceId: null
63
67
  };
@@ -85,6 +89,7 @@ function createInitialState() {
85
89
  programmaticRetryActive: false,
86
90
  programmaticRetryCount: 0,
87
91
  programmaticLastError: null,
92
+ adDeliveryProfile: null,
88
93
  initialized: false,
89
94
  screenOrientation: null,
90
95
  screenDimensions: null,
@@ -102,6 +107,7 @@ function getPublicState(internal) {
102
107
  programmaticPlaying: internal.programmaticPlaying,
103
108
  prefetchedReady: internal.prefetchedReady ?? false,
104
109
  waterfallMode: internal.waterfallMode,
110
+ adDeliveryProfileMode: internal.adDeliveryProfile?.mode ?? null,
105
111
  screenId: internal.screenId,
106
112
  deviceId: internal.deviceId
107
113
  };
@@ -362,10 +368,9 @@ var ApiClient = class {
362
368
  }
363
369
  }
364
370
  /**
365
- * Ping the heartbeat endpoint to signal this device is alive.
366
- * Returns `true` on 2xx, `false` on any error.
371
+ * Ping heartbeat endpoint and return delivery profile + queued commands.
367
372
  */
368
- async sendHeartbeat(deviceId, screenId) {
373
+ async sendHeartbeat(deviceId, screenId, payload = {}) {
369
374
  logger.debug("Sending heartbeat", { deviceId });
370
375
  try {
371
376
  const response = await fetch(`${this.apiBase}/device/${deviceId}/heartbeat`, {
@@ -375,13 +380,24 @@ var ApiClient = class {
375
380
  device_id: deviceId,
376
381
  screen_id: screenId,
377
382
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
378
- status: "active"
383
+ status: "active",
384
+ ...payload
379
385
  }),
380
386
  signal: AbortSignal.timeout(5e3)
381
387
  });
382
- return response.ok;
388
+ if (!response.ok) {
389
+ return { ok: false, data: null };
390
+ }
391
+ let data = null;
392
+ try {
393
+ const json = await response.json();
394
+ data = json?.data ?? null;
395
+ } catch {
396
+ data = null;
397
+ }
398
+ return { ok: true, data };
383
399
  } catch {
384
- return false;
400
+ return { ok: false, data: null };
385
401
  }
386
402
  }
387
403
  /**
@@ -934,7 +950,7 @@ var WaterfallEngine = class {
934
950
  };
935
951
 
936
952
  // src/player/ProgrammaticPlayer.ts
937
- var ProgrammaticPlayer = class {
953
+ var _ProgrammaticPlayer = class _ProgrammaticPlayer {
938
954
  constructor(events, timeoutMs = 12e3) {
939
955
  // ── Public state ──────────────────────────────────────────
940
956
  this.vastTagUrl = null;
@@ -1021,8 +1037,11 @@ var ProgrammaticPlayer = class {
1021
1037
  this.telemetry.recordRequest();
1022
1038
  const correlator = Date.now();
1023
1039
  const finalUrl = vastUrl.includes("correlator=") ? vastUrl.replace(/correlator=[^&]*/, `correlator=${correlator}`) : `${vastUrl}${vastUrl.includes("?") ? "&" : "?"}correlator=${correlator}`;
1024
- if (typeof google === "undefined" || !google?.ima) {
1025
- onError("Google IMA SDK not loaded");
1040
+ const imaReady = await this.ensureImaSdk();
1041
+ if (!imaReady) {
1042
+ const msg = "Google IMA SDK not loaded";
1043
+ this.events.emit("programmatic_error", { error: msg, code: void 0 });
1044
+ onError(msg);
1026
1045
  this.telemetry.recordError();
1027
1046
  this.waterfallEngine.recordFailure(sourceName);
1028
1047
  return;
@@ -1039,6 +1058,50 @@ var ProgrammaticPlayer = class {
1039
1058
  onError(err instanceof Error ? err.message : String(err));
1040
1059
  }
1041
1060
  }
1061
+ /**
1062
+ * Ensure Google IMA SDK is available. If already loaded, resolves
1063
+ * immediately. Otherwise injects the `<script>` tag and waits.
1064
+ * Matches the Lite SDK's `loadImaScript()` pattern.
1065
+ */
1066
+ async ensureImaSdk() {
1067
+ if (typeof google !== "undefined" && google?.ima) return true;
1068
+ if (typeof document === "undefined") return false;
1069
+ const existing = document.querySelector(
1070
+ `script[src="${_ProgrammaticPlayer.IMA_SDK_URL}"]`
1071
+ );
1072
+ if (existing) {
1073
+ return new Promise((resolve) => {
1074
+ const start = Date.now();
1075
+ const check = () => {
1076
+ if (typeof google !== "undefined" && google?.ima) {
1077
+ resolve(true);
1078
+ } else if (Date.now() - start > 5e3) {
1079
+ resolve(false);
1080
+ } else {
1081
+ setTimeout(check, 100);
1082
+ }
1083
+ };
1084
+ check();
1085
+ });
1086
+ }
1087
+ if (!_ProgrammaticPlayer.imaLoadPromise) {
1088
+ _ProgrammaticPlayer.imaLoadPromise = new Promise((resolve) => {
1089
+ const script = document.createElement("script");
1090
+ script.src = _ProgrammaticPlayer.IMA_SDK_URL;
1091
+ script.async = true;
1092
+ script.onload = () => {
1093
+ _ProgrammaticPlayer.imaLoadPromise = null;
1094
+ resolve(true);
1095
+ };
1096
+ script.onerror = () => {
1097
+ _ProgrammaticPlayer.imaLoadPromise = null;
1098
+ resolve(false);
1099
+ };
1100
+ document.head.appendChild(script);
1101
+ });
1102
+ }
1103
+ return _ProgrammaticPlayer.imaLoadPromise;
1104
+ }
1042
1105
  // ── IMA request / playback lifecycle ──────────────────────
1043
1106
  async requestAdsViaIMA(vastUrl, onComplete, onError) {
1044
1107
  return new Promise((resolve) => {
@@ -1240,6 +1303,12 @@ var ProgrammaticPlayer = class {
1240
1303
  this.telemetry.reset();
1241
1304
  }
1242
1305
  };
1306
+ // ── IMA SDK loader ──────────────────────────────────────────
1307
+ /** IMA script URL */
1308
+ _ProgrammaticPlayer.IMA_SDK_URL = "https://imasdk.googleapis.com/js/sdkloader/ima3.js";
1309
+ /** Singleton load promise so concurrent calls don't insert multiple scripts. */
1310
+ _ProgrammaticPlayer.imaLoadPromise = null;
1311
+ var ProgrammaticPlayer = _ProgrammaticPlayer;
1243
1312
 
1244
1313
  // src/player/Player.ts
1245
1314
  var MAX_AD_DURATION_SECONDS = 300;
@@ -1674,11 +1743,104 @@ var _TrillboardsAds = class _TrillboardsAds {
1674
1743
  }
1675
1744
  startHeartbeatTimer() {
1676
1745
  if (this.state.heartbeatTimer) clearInterval(this.state.heartbeatTimer);
1746
+ const tick = () => {
1747
+ this.sendHeartbeatTick().catch(() => {
1748
+ });
1749
+ };
1750
+ tick();
1677
1751
  this.state.heartbeatTimer = setInterval(() => {
1678
- this.api.sendHeartbeat(this.config.deviceId, this.state.screenId);
1752
+ tick();
1679
1753
  }, this.config.heartbeatInterval);
1680
1754
  }
1755
+ async sendHeartbeatTick() {
1756
+ const result = await this.api.sendHeartbeat(
1757
+ this.config.deviceId,
1758
+ this.state.screenId,
1759
+ this.buildHeartbeatPayload()
1760
+ );
1761
+ if (!result.ok || !result.data) return;
1762
+ if (result.data.ad_delivery_profile?.mode) {
1763
+ this.applyAdDeliveryProfile(result.data.ad_delivery_profile);
1764
+ }
1765
+ if (Array.isArray(result.data.commands) && result.data.commands.length > 0) {
1766
+ this.runHeartbeatCommands(result.data.commands);
1767
+ }
1768
+ }
1769
+ buildHeartbeatPayload() {
1770
+ const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "";
1771
+ const platform = typeof navigator !== "undefined" ? navigator.platform : null;
1772
+ const connection = typeof navigator !== "undefined" ? navigator.connection : null;
1773
+ const chromiumMatch = userAgent.match(/(?:Chrome|Chromium)\/(\d+)/i);
1774
+ const chromiumMajor = chromiumMatch ? Number(chromiumMatch[1]) : void 0;
1775
+ const imaSupported = typeof window !== "undefined" ? Boolean(window?.google?.ima) : null;
1776
+ const telemetry = {
1777
+ powerState: typeof document !== "undefined" && document.hidden ? "standby" : "on",
1778
+ agentStatus: typeof document !== "undefined" && document.hidden ? "background" : "foreground",
1779
+ os: platform || void 0,
1780
+ agentVersion: SDK_VERSION,
1781
+ ima_integration: "html5_webview",
1782
+ ima_supported: imaSupported,
1783
+ webview_chromium_major: Number.isFinite(chromiumMajor) ? chromiumMajor : void 0
1784
+ };
1785
+ const payload = {
1786
+ telemetry,
1787
+ sdk: {
1788
+ version: SDK_VERSION,
1789
+ ima_supported: imaSupported,
1790
+ ima_integration: "html5_webview",
1791
+ webview_chromium_major: Number.isFinite(chromiumMajor) ? chromiumMajor : void 0
1792
+ },
1793
+ device: {
1794
+ os: platform || void 0,
1795
+ model: userAgent || void 0
1796
+ }
1797
+ };
1798
+ if (connection) {
1799
+ payload.network = {
1800
+ connectionType: connection.type || void 0,
1801
+ effectiveType: connection.effectiveType || void 0,
1802
+ downlinkMbps: typeof connection.downlink === "number" ? connection.downlink : void 0,
1803
+ bandwidthMbps: typeof connection.downlink === "number" ? connection.downlink : void 0,
1804
+ rtt: typeof connection.rtt === "number" ? connection.rtt : void 0,
1805
+ latencyRtt: typeof connection.rtt === "number" ? connection.rtt : void 0,
1806
+ saveData: typeof connection.saveData === "boolean" ? connection.saveData : void 0
1807
+ };
1808
+ }
1809
+ return payload;
1810
+ }
1811
+ applyAdDeliveryProfile(profile) {
1812
+ if (!profile?.mode) return;
1813
+ const previousMode = this.state.adDeliveryProfile?.mode ?? null;
1814
+ this.state.adDeliveryProfile = profile;
1815
+ if (profile.mode === "vast_fallback" && this.state.programmaticPlaying) {
1816
+ this.programmaticPlayer.stop({ silent: true });
1817
+ this.state.programmaticPlaying = false;
1818
+ }
1819
+ if (previousMode !== profile.mode) {
1820
+ this.emitStateChanged();
1821
+ }
1822
+ }
1823
+ runHeartbeatCommands(commands) {
1824
+ for (const command of commands) {
1825
+ const name = String(command?.command || "").toLowerCase();
1826
+ if (name === "refresh_ads") {
1827
+ this.refresh().catch(() => {
1828
+ });
1829
+ } else if (name === "restart") {
1830
+ if (typeof window !== "undefined" && typeof window.location?.reload === "function") {
1831
+ window.location.reload();
1832
+ return;
1833
+ }
1834
+ this.refresh().catch(() => {
1835
+ });
1836
+ }
1837
+ }
1838
+ }
1681
1839
  playNextAd() {
1840
+ if (this.state.adDeliveryProfile?.mode === "vast_fallback" && this.state.ads.length > 0) {
1841
+ this.playDirect();
1842
+ return;
1843
+ }
1682
1844
  const mode = this.state.waterfallMode;
1683
1845
  if (mode === "programmatic_only" || mode === "programmatic_then_direct") {
1684
1846
  this.playProgrammatic();
@@ -1693,13 +1855,20 @@ var _TrillboardsAds = class _TrillboardsAds {
1693
1855
  this.programmaticPlayer.play(
1694
1856
  () => {
1695
1857
  this.state.programmaticPlaying = false;
1858
+ this.resetProgrammaticBackoff();
1696
1859
  this.emitStateChanged();
1697
1860
  this.scheduleProgrammaticRetry();
1698
1861
  },
1699
1862
  (error) => {
1700
1863
  this.state.programmaticPlaying = false;
1701
1864
  this.state.programmaticLastError = error;
1702
- this.state.programmaticRetryCount++;
1865
+ const normalizedError = String(error || "").toLowerCase();
1866
+ const isNoFillLike = normalizedError.includes("no fill") || normalizedError.includes("no ads vast response") || normalizedError.includes("waterfall_exhausted") || normalizedError === "throttled" || normalizedError === "busy";
1867
+ if (isNoFillLike) {
1868
+ this.state.programmaticRetryCount = 0;
1869
+ } else {
1870
+ this.state.programmaticRetryCount++;
1871
+ }
1703
1872
  this.emitStateChanged();
1704
1873
  if (this.state.waterfallMode === "programmatic_then_direct" && this.state.ads.length > 0) {
1705
1874
  this.playDirect();