@trillboards/ads-sdk 2.1.0 → 2.2.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.
@@ -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":"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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 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
  };
@@ -227,6 +233,9 @@ var Logger = class {
227
233
  }
228
234
  };
229
235
  var logger = new Logger();
236
+ function setLogLevel(level) {
237
+ logger.setLevel(level);
238
+ }
230
239
 
231
240
  // src/api/client.ts
232
241
  function getPlayerTruth(container) {
@@ -359,10 +368,9 @@ var ApiClient = class {
359
368
  }
360
369
  }
361
370
  /**
362
- * Ping the heartbeat endpoint to signal this device is alive.
363
- * Returns `true` on 2xx, `false` on any error.
371
+ * Ping heartbeat endpoint and return delivery profile + queued commands.
364
372
  */
365
- async sendHeartbeat(deviceId, screenId) {
373
+ async sendHeartbeat(deviceId, screenId, payload = {}) {
366
374
  logger.debug("Sending heartbeat", { deviceId });
367
375
  try {
368
376
  const response = await fetch(`${this.apiBase}/device/${deviceId}/heartbeat`, {
@@ -372,13 +380,24 @@ var ApiClient = class {
372
380
  device_id: deviceId,
373
381
  screen_id: screenId,
374
382
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
375
- status: "active"
383
+ status: "active",
384
+ ...payload
376
385
  }),
377
386
  signal: AbortSignal.timeout(5e3)
378
387
  });
379
- 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 };
380
399
  } catch {
381
- return false;
400
+ return { ok: false, data: null };
382
401
  }
383
402
  }
384
403
  /**
@@ -1395,6 +1414,9 @@ var _TrillboardsAds = class _TrillboardsAds {
1395
1414
  this.offlineHandler = null;
1396
1415
  this.visibilityHandler = null;
1397
1416
  this.config = resolveConfig(config);
1417
+ if (this.config.debug) {
1418
+ setLogLevel("debug");
1419
+ }
1398
1420
  this.state = createInitialState();
1399
1421
  this.events = new EventEmitter();
1400
1422
  this.api = new ApiClient(this.config.apiBase);
@@ -1668,11 +1690,104 @@ var _TrillboardsAds = class _TrillboardsAds {
1668
1690
  }
1669
1691
  startHeartbeatTimer() {
1670
1692
  if (this.state.heartbeatTimer) clearInterval(this.state.heartbeatTimer);
1693
+ const tick = () => {
1694
+ this.sendHeartbeatTick().catch(() => {
1695
+ });
1696
+ };
1697
+ tick();
1671
1698
  this.state.heartbeatTimer = setInterval(() => {
1672
- this.api.sendHeartbeat(this.config.deviceId, this.state.screenId);
1699
+ tick();
1673
1700
  }, this.config.heartbeatInterval);
1674
1701
  }
1702
+ async sendHeartbeatTick() {
1703
+ const result = await this.api.sendHeartbeat(
1704
+ this.config.deviceId,
1705
+ this.state.screenId,
1706
+ this.buildHeartbeatPayload()
1707
+ );
1708
+ if (!result.ok || !result.data) return;
1709
+ if (result.data.ad_delivery_profile?.mode) {
1710
+ this.applyAdDeliveryProfile(result.data.ad_delivery_profile);
1711
+ }
1712
+ if (Array.isArray(result.data.commands) && result.data.commands.length > 0) {
1713
+ this.runHeartbeatCommands(result.data.commands);
1714
+ }
1715
+ }
1716
+ buildHeartbeatPayload() {
1717
+ const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "";
1718
+ const platform = typeof navigator !== "undefined" ? navigator.platform : null;
1719
+ const connection = typeof navigator !== "undefined" ? navigator.connection : null;
1720
+ const chromiumMatch = userAgent.match(/(?:Chrome|Chromium)\/(\d+)/i);
1721
+ const chromiumMajor = chromiumMatch ? Number(chromiumMatch[1]) : void 0;
1722
+ const imaSupported = typeof window !== "undefined" ? Boolean(window?.google?.ima) : null;
1723
+ const telemetry = {
1724
+ powerState: typeof document !== "undefined" && document.hidden ? "standby" : "on",
1725
+ agentStatus: typeof document !== "undefined" && document.hidden ? "background" : "foreground",
1726
+ os: platform || void 0,
1727
+ agentVersion: SDK_VERSION,
1728
+ ima_integration: "html5_webview",
1729
+ ima_supported: imaSupported,
1730
+ webview_chromium_major: Number.isFinite(chromiumMajor) ? chromiumMajor : void 0
1731
+ };
1732
+ const payload = {
1733
+ telemetry,
1734
+ sdk: {
1735
+ version: SDK_VERSION,
1736
+ ima_supported: imaSupported,
1737
+ ima_integration: "html5_webview",
1738
+ webview_chromium_major: Number.isFinite(chromiumMajor) ? chromiumMajor : void 0
1739
+ },
1740
+ device: {
1741
+ os: platform || void 0,
1742
+ model: userAgent || void 0
1743
+ }
1744
+ };
1745
+ if (connection) {
1746
+ payload.network = {
1747
+ connectionType: connection.type || void 0,
1748
+ effectiveType: connection.effectiveType || void 0,
1749
+ downlinkMbps: typeof connection.downlink === "number" ? connection.downlink : void 0,
1750
+ bandwidthMbps: typeof connection.downlink === "number" ? connection.downlink : void 0,
1751
+ rtt: typeof connection.rtt === "number" ? connection.rtt : void 0,
1752
+ latencyRtt: typeof connection.rtt === "number" ? connection.rtt : void 0,
1753
+ saveData: typeof connection.saveData === "boolean" ? connection.saveData : void 0
1754
+ };
1755
+ }
1756
+ return payload;
1757
+ }
1758
+ applyAdDeliveryProfile(profile) {
1759
+ if (!profile?.mode) return;
1760
+ const previousMode = this.state.adDeliveryProfile?.mode ?? null;
1761
+ this.state.adDeliveryProfile = profile;
1762
+ if (profile.mode === "vast_fallback" && this.state.programmaticPlaying) {
1763
+ this.programmaticPlayer.stop({ silent: true });
1764
+ this.state.programmaticPlaying = false;
1765
+ }
1766
+ if (previousMode !== profile.mode) {
1767
+ this.emitStateChanged();
1768
+ }
1769
+ }
1770
+ runHeartbeatCommands(commands) {
1771
+ for (const command of commands) {
1772
+ const name = String(command?.command || "").toLowerCase();
1773
+ if (name === "refresh_ads") {
1774
+ this.refresh().catch(() => {
1775
+ });
1776
+ } else if (name === "restart") {
1777
+ if (typeof window !== "undefined" && typeof window.location?.reload === "function") {
1778
+ window.location.reload();
1779
+ return;
1780
+ }
1781
+ this.refresh().catch(() => {
1782
+ });
1783
+ }
1784
+ }
1785
+ }
1675
1786
  playNextAd() {
1787
+ if (this.state.adDeliveryProfile?.mode === "vast_fallback" && this.state.ads.length > 0) {
1788
+ this.playDirect();
1789
+ return;
1790
+ }
1676
1791
  const mode = this.state.waterfallMode;
1677
1792
  if (mode === "programmatic_only" || mode === "programmatic_then_direct") {
1678
1793
  this.playProgrammatic();
@@ -1687,13 +1802,20 @@ var _TrillboardsAds = class _TrillboardsAds {
1687
1802
  this.programmaticPlayer.play(
1688
1803
  () => {
1689
1804
  this.state.programmaticPlaying = false;
1805
+ this.resetProgrammaticBackoff();
1690
1806
  this.emitStateChanged();
1691
1807
  this.scheduleProgrammaticRetry();
1692
1808
  },
1693
1809
  (error) => {
1694
1810
  this.state.programmaticPlaying = false;
1695
1811
  this.state.programmaticLastError = error;
1696
- this.state.programmaticRetryCount++;
1812
+ const normalizedError = String(error || "").toLowerCase();
1813
+ const isNoFillLike = normalizedError.includes("no fill") || normalizedError.includes("no ads vast response") || normalizedError.includes("waterfall_exhausted") || normalizedError === "throttled" || normalizedError === "busy";
1814
+ if (isNoFillLike) {
1815
+ this.state.programmaticRetryCount = 0;
1816
+ } else {
1817
+ this.state.programmaticRetryCount++;
1818
+ }
1697
1819
  this.emitStateChanged();
1698
1820
  if (this.state.waterfallMode === "programmatic_then_direct" && this.state.ads.length > 0) {
1699
1821
  this.playDirect();