@trillboards/ads-sdk 2.1.1 → 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":";;;;;;;;;;;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,GAAqBA,gBAAA;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,GAAaC,aAAoB,IAAI,CAAA;AAC3C,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,eAAS,KAAK,CAAA;AAG5C,IAAA,MAAM,UAAA,GAAaD,aAAO,OAAO,CAAA;AACjC,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,IAAA,MAAM,cAAA,GAAiBA,aAAO,WAAW,CAAA;AACzC,IAAA,cAAA,CAAe,OAAA,GAAU,WAAA;AACzB,IAAA,MAAM,YAAA,GAAeA,aAAO,SAAS,CAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AACvB,IAAA,MAAM,YAAA,GAAeA,aAAO,SAAS,CAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AACvB,IAAA,MAAM,wBAAA,GAA2BA,aAAO,qBAAqB,CAAA;AAC7D,IAAA,wBAAA,CAAyB,OAAA,GAAU,qBAAA;AACnC,IAAA,MAAM,sBAAA,GAAyBA,aAAO,mBAAmB,CAAA;AACzD,IAAA,sBAAA,CAAuB,OAAA,GAAU,mBAAA;AACjC,IAAA,MAAM,iBAAA,GAAoBA,aAAO,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,GAAcE,iBAAA;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,IAAAC,yBAAA;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,GAAgBD,iBAAA;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,uBACEE,cAAA;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,GAAMJ,aAAiC,IAAI,CAAA;AACjD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,eAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAA2B,oBAAoB,CAAA;AAEzE,EAAA,MAAM,IAAA,GAAOC,kBAAY,MAAM,GAAA,CAAI,SAAS,IAAA,EAAK,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,IAAA,GAAOA,kBAAY,MAAM,GAAA,CAAI,SAAS,IAAA,EAAK,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,MAAA,GAASA,kBAAY,MAAM,GAAA,CAAI,SAAS,MAAA,EAAO,EAAG,EAAE,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAUA,kBAAY,MAAM,GAAA,CAAI,SAAS,OAAA,EAAQ,EAAG,EAAE,CAAA;AAC5D,EAAA,MAAM,SAAA,GAAYA,iBAAAA;AAAA,IAChB,CAAC,MAAA,KAA2D,GAAA,CAAI,OAAA,EAAS,UAAU,MAAM,CAAA;AAAA,IACzF;AAAC,GACH;AAEA,EAAA,MAAM,UAAUA,iBAAAA,CAAY,MAAM,WAAW,IAAI,CAAA,EAAG,EAAE,CAAA;AACtD,EAAA,MAAM,cAAA,GAAiBA,kBAAY,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.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
  };
@@ -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
  /**
@@ -1674,11 +1690,104 @@ var _TrillboardsAds = class _TrillboardsAds {
1674
1690
  }
1675
1691
  startHeartbeatTimer() {
1676
1692
  if (this.state.heartbeatTimer) clearInterval(this.state.heartbeatTimer);
1693
+ const tick = () => {
1694
+ this.sendHeartbeatTick().catch(() => {
1695
+ });
1696
+ };
1697
+ tick();
1677
1698
  this.state.heartbeatTimer = setInterval(() => {
1678
- this.api.sendHeartbeat(this.config.deviceId, this.state.screenId);
1699
+ tick();
1679
1700
  }, this.config.heartbeatInterval);
1680
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
+ }
1681
1786
  playNextAd() {
1787
+ if (this.state.adDeliveryProfile?.mode === "vast_fallback" && this.state.ads.length > 0) {
1788
+ this.playDirect();
1789
+ return;
1790
+ }
1682
1791
  const mode = this.state.waterfallMode;
1683
1792
  if (mode === "programmatic_only" || mode === "programmatic_then_direct") {
1684
1793
  this.playProgrammatic();
@@ -1693,13 +1802,20 @@ var _TrillboardsAds = class _TrillboardsAds {
1693
1802
  this.programmaticPlayer.play(
1694
1803
  () => {
1695
1804
  this.state.programmaticPlaying = false;
1805
+ this.resetProgrammaticBackoff();
1696
1806
  this.emitStateChanged();
1697
1807
  this.scheduleProgrammaticRetry();
1698
1808
  },
1699
1809
  (error) => {
1700
1810
  this.state.programmaticPlaying = false;
1701
1811
  this.state.programmaticLastError = error;
1702
- 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
+ }
1703
1819
  this.emitStateChanged();
1704
1820
  if (this.state.waterfallMode === "programmatic_then_direct" && this.state.ads.length > 0) {
1705
1821
  this.playDirect();