@trillboards/ads-sdk 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +158 -0
- package/dist/index.d.mts +602 -0
- package/dist/index.d.ts +602 -0
- package/dist/index.js +1752 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1739 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-native.d.mts +154 -0
- package/dist/react-native.d.ts +154 -0
- package/dist/react-native.js +193 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +190 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/react.d.mts +239 -0
- package/dist/react.d.ts +239 -0
- package/dist/react.js +1891 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +1885 -0
- package/dist/react.mjs.map +1 -0
- package/dist/server.d.mts +238 -0
- package/dist/server.d.ts +238 -0
- package/dist/server.js +209 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +200 -0
- package/dist/server.mjs.map +1 -0
- package/dist/trillboards-lite.global.js +2 -0
- package/dist/trillboards-lite.global.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/iife.ts","../src/core/config.ts","../src/core/state.ts","../src/core/events.ts","../src/api/client.ts","../src/cache/IndexedDBCache.ts","../src/bridge/NativeBridge.ts","../src/player/CircuitBreaker.ts","../src/player/Telemetry.ts","../src/player/WaterfallEngine.ts","../src/player/ProgrammaticPlayer.ts","../src/player/Player.ts","../src/core/TrillboardsAds.ts"],"sourcesContent":["/**\n * IIFE entry point for backwards compatibility.\n * This builds to dist/trillboards-lite.js and replaces the static file\n * at trillboard-screen/public/partner-sdk/trillboards-lite.js.\n *\n * It exposes window.TrillboardsLite with the same API as the original SDK.\n */\nimport { TrillboardsAds } from './core/TrillboardsAds';\nimport { SDK_VERSION } from './core/config';\nimport { DEFAULT_PUBLIC_STATE } from './core/state';\nimport type { TrillboardsConfig, BridgeConfig, TrillboardsState, AdItem, EventMap } from './core/types';\n\ninterface LiteInitConfig {\n deviceId: string;\n container?: HTMLElement | string;\n waterfall?: string;\n autoStart?: boolean;\n apiBase?: string;\n}\n\ninterface TrillboardsLiteAPI {\n _instance: TrillboardsAds | null;\n init: (config: LiteInitConfig) => Promise<void>;\n show: () => void;\n hide: () => void;\n skipAd: () => void;\n refresh: () => Promise<void>;\n prefetchNextAd: () => Promise<void>;\n getState: () => TrillboardsState;\n getAds: () => AdItem[];\n on: <K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void) => void;\n off: <K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void) => void;\n registerBridge: (config: BridgeConfig) => boolean;\n destroy: () => void;\n VERSION: string;\n}\n\nconst TrillboardsLite: TrillboardsLiteAPI = {\n VERSION: SDK_VERSION,\n\n _instance: null as TrillboardsAds | null,\n\n async init(config: LiteInitConfig) {\n const sdkConfig: TrillboardsConfig = {\n deviceId: config.deviceId,\n waterfall: (config.waterfall as any) ?? 'programmatic_only',\n autoStart: config.autoStart ?? true,\n apiBase: config.apiBase,\n };\n\n this._instance = await TrillboardsAds.create(sdkConfig);\n },\n\n show() {\n this._instance?.show();\n },\n\n hide() {\n this._instance?.hide();\n },\n\n skipAd() {\n this._instance?.skipAd();\n },\n\n async refresh() {\n await this._instance?.refresh();\n },\n\n async prefetchNextAd() {\n await this._instance?.prefetchNextAd();\n },\n\n getState() {\n return this._instance?.getState() ?? { ...DEFAULT_PUBLIC_STATE };\n },\n\n getAds() {\n return this._instance?.getAds() ?? [];\n },\n\n on(event: any, handler: any) {\n this._instance?.on(event, handler);\n },\n\n off(event: any, handler: any) {\n this._instance?.off(event, handler);\n },\n\n registerBridge(config: BridgeConfig) {\n return this._instance?.registerBridge(config) ?? false;\n },\n\n destroy() {\n this._instance?.destroy();\n this._instance = null;\n },\n};\n\n// Expose on window for backwards compatibility\nif (typeof window !== 'undefined') {\n (window as any).TrillboardsLite = TrillboardsLite;\n}\n\nexport { TrillboardsLite };\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Default configuration & resolver\n// ─────────────────────────────────────────────────────────────\n\nimport type { TrillboardsConfig } from './types';\n\n/** Single source of truth for the SDK version string. */\nexport const SDK_VERSION = '2.0.0';\n\n/**\n * Compile-time constants that mirror the original IIFE CONFIG\n * object. Every value here acts as a sensible production default\n * that can be overridden per-device via TrillboardsConfig.\n */\nexport const DEFAULT_CONFIG = {\n API_BASE: 'https://api.trillboards.com/v1/partner',\n CDN_BASE: 'https://cdn.trillboards.com',\n CACHE_NAME: 'trillboards-lite-ads',\n CACHE_SIZE: 10,\n REFRESH_INTERVAL: 2 * 60 * 1000, // 2 minutes\n HEARTBEAT_INTERVAL: 60 * 1000, // 1 minute\n DEFAULT_IMAGE_DURATION: 8000, // 8 seconds\n DEFAULT_AD_INTERVAL: 60000, // 1 minute\n PROGRAMMATIC_TIMEOUT_MS: 12000, // 12 seconds\n PROGRAMMATIC_MIN_INTERVAL_MS: 5000, // 5 seconds\n PROGRAMMATIC_RETRY_MS: 5000, // 5 seconds\n PROGRAMMATIC_BACKOFF_MAX_MS: 5 * 60 * 1000, // 5 minutes\n VERSION: SDK_VERSION,\n} as const;\n\n/**\n * Merge a caller-supplied TrillboardsConfig with the defaults,\n * returning a fully-resolved configuration object with no\n * optional gaps.\n *\n * Throws if `deviceId` is empty or whitespace-only.\n * Numeric fields are clamped to sensible minimums.\n */\nexport function resolveConfig(userConfig: TrillboardsConfig) {\n const deviceId = userConfig.deviceId?.trim();\n if (!deviceId) {\n throw new Error('TrillboardsConfig: deviceId must be a non-empty string');\n }\n\n return {\n deviceId,\n apiBase: userConfig.apiBase ?? DEFAULT_CONFIG.API_BASE,\n cdnBase: userConfig.cdnBase ?? DEFAULT_CONFIG.CDN_BASE,\n waterfall: userConfig.waterfall ?? 'programmatic_only',\n autoStart: userConfig.autoStart ?? true,\n refreshInterval: Math.max(userConfig.refreshInterval ?? DEFAULT_CONFIG.REFRESH_INTERVAL, 10000),\n heartbeatInterval: Math.max(userConfig.heartbeatInterval ?? DEFAULT_CONFIG.HEARTBEAT_INTERVAL, 5000),\n defaultImageDuration: Math.max(userConfig.defaultImageDuration ?? DEFAULT_CONFIG.DEFAULT_IMAGE_DURATION, 1000),\n defaultAdInterval: Math.max(userConfig.defaultAdInterval ?? DEFAULT_CONFIG.DEFAULT_AD_INTERVAL, 1000),\n programmaticTimeout: Math.max(userConfig.programmaticTimeout ?? DEFAULT_CONFIG.PROGRAMMATIC_TIMEOUT_MS, 1000),\n programmaticMinInterval: Math.max(userConfig.programmaticMinInterval ?? DEFAULT_CONFIG.PROGRAMMATIC_MIN_INTERVAL_MS, 1000),\n programmaticRetry: Math.max(userConfig.programmaticRetry ?? DEFAULT_CONFIG.PROGRAMMATIC_RETRY_MS, 1000),\n programmaticBackoffMax: Math.max(userConfig.programmaticBackoffMax ?? DEFAULT_CONFIG.PROGRAMMATIC_BACKOFF_MAX_MS, 5000),\n cacheSize: Math.max(userConfig.cacheSize ?? DEFAULT_CONFIG.CACHE_SIZE, 1),\n } as const;\n}\n\n/** The fully-resolved config shape after defaults are applied. */\nexport type ResolvedConfig = ReturnType<typeof resolveConfig>;\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","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Typed event emitter\n// ─────────────────────────────────────────────────────────────\n\nimport type { EventMap } from './types';\n\ntype Handler<T> = (data: T) => void;\n\n/**\n * A lightweight, fully-typed event emitter.\n *\n * Usage:\n * ```ts\n * const emitter = new EventEmitter();\n * emitter.on('ad_started', ({ id, type, index }) => { ... });\n * emitter.emit('ad_started', { id: '123', type: 'video', index: 0 });\n * ```\n *\n * Handler errors are caught and logged so a single bad listener\n * can never crash the SDK or block other listeners.\n */\nexport class EventEmitter {\n private handlers: Map<string, Set<Handler<any>>> = new Map();\n\n /**\n * Register a handler for the given event.\n * The same handler reference can only be registered once per\n * event (Set semantics).\n */\n on<K extends keyof EventMap>(event: K, handler: Handler<EventMap[K]>): void {\n if (!this.handlers.has(event as string)) {\n this.handlers.set(event as string, new Set());\n }\n this.handlers.get(event as string)!.add(handler);\n }\n\n /**\n * Remove a previously registered handler.\n * No-op if the handler was never registered.\n */\n off<K extends keyof EventMap>(event: K, handler: Handler<EventMap[K]>): void {\n this.handlers.get(event as string)?.delete(handler);\n }\n\n /**\n * Emit an event, invoking every registered handler with the\n * supplied data payload. Handlers are called synchronously in\n * registration order. Exceptions inside handlers are caught\n * and logged to the console so they never propagate.\n */\n emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {\n const handlers = this.handlers.get(event as string);\n if (!handlers) return;\n for (const handler of [...handlers]) {\n try {\n handler(data);\n } catch (err) {\n console.error(\n `[TrillboardsAds] Event handler error for \"${String(event)}\":`,\n err,\n );\n }\n }\n }\n\n /**\n * Register a one-shot handler that automatically removes itself\n * after the first invocation.\n */\n once<K extends keyof EventMap>(event: K, handler: Handler<EventMap[K]>): void {\n const wrapper: Handler<EventMap[K]> = (data) => {\n this.off(event, wrapper);\n handler(data);\n };\n this.on(event, wrapper);\n }\n\n /**\n * Remove all handlers for all events.\n * Typically called during SDK teardown / destroy.\n */\n removeAllListeners(): void {\n this.handlers.clear();\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — API client (fetch ads, heartbeat,\n// impression tracking, programmatic events)\n// ─────────────────────────────────────────────────────────────\n\nimport type { FetchAdsResult, PlayerTruth } from '../core/types';\nimport { DEFAULT_CONFIG } from '../core/config';\n\n/**\n * Measure the actual player container (or fall back to the\n * viewport) and return a `PlayerTruth` snapshot the server\n * uses for creative selection and sizing.\n *\n * Returns safe SSR defaults when `window` is not available.\n */\nfunction getPlayerTruth(container?: HTMLElement | null): PlayerTruth {\n if (typeof window === 'undefined') {\n return {\n slotWidth: null,\n slotHeight: null,\n orientation: null,\n muted: true,\n autoplayAllowed: true,\n userAgent: null,\n };\n }\n\n let width = window.innerWidth;\n let height = window.innerHeight;\n if (container) {\n const rect = container.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n width = Math.round(rect.width);\n height = Math.round(rect.height);\n }\n }\n return {\n slotWidth: width,\n slotHeight: height,\n orientation: height > width ? 'portrait' : 'landscape',\n muted: true,\n autoplayAllowed: true,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : null,\n };\n}\n\n/**\n * Low-level HTTP client for the Trillboards Partner API.\n *\n * Each method maps 1-to-1 to an endpoint:\n * - `fetchAds` — `GET /device/:id/ads`\n * - `reportImpression` — `GET /impression`\n * - `sendHeartbeat` — `POST /device/:id/heartbeat`\n * - `reportProgrammaticEvent` — `POST /programmatic-event`\n */\nexport class ApiClient {\n private apiBase: string;\n private container: HTMLElement | null = null;\n\n constructor(apiBase?: string) {\n this.apiBase = apiBase ?? DEFAULT_CONFIG.API_BASE;\n }\n\n /** Bind an optional container element for slot-size measurement. */\n setContainer(container: HTMLElement | null) {\n this.container = container;\n }\n\n /**\n * Fetch the current ad roster for a device.\n *\n * Supports conditional requests via `ETag` / `If-None-Match` —\n * when the server returns 304 the caller receives the previous\n * ad list untouched (indicated by `notModified: true`).\n *\n * The request is aborted after 10 seconds.\n */\n async fetchAds(deviceId: string, etag: string | null = null): Promise<FetchAdsResult> {\n const headers: Record<string, string> = { Accept: 'application/json' };\n if (etag) headers['If-None-Match'] = etag;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n try {\n const playerTruth = getPlayerTruth(this.container);\n const params = new URLSearchParams();\n if (playerTruth.slotWidth) params.set('slot_w', String(playerTruth.slotWidth));\n if (playerTruth.slotHeight) params.set('slot_h', String(playerTruth.slotHeight));\n if (playerTruth.orientation) params.set('orientation', playerTruth.orientation);\n params.set('muted', playerTruth.muted ? '1' : '0');\n params.set('autoplay', playerTruth.autoplayAllowed ? '1' : '0');\n if (playerTruth.userAgent) params.set('ua', playerTruth.userAgent);\n\n const query = params.toString();\n const url = query\n ? `${this.apiBase}/device/${deviceId}/ads?${query}`\n : `${this.apiBase}/device/${deviceId}/ads`;\n\n const response = await fetch(url, { headers, signal: controller.signal });\n clearTimeout(timeoutId);\n\n if (response.status === 304) {\n return {\n ads: [],\n settings: {},\n programmatic: null,\n screenId: null,\n screenOrientation: null,\n screenDimensions: null,\n etag,\n notModified: true,\n };\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json();\n const newEtag = response.headers.get('ETag');\n\n return {\n ads: data.data?.ads ?? [],\n settings: data.data?.settings ?? {},\n programmatic: data.data?.header_bidding_settings ?? null,\n screenId: data.data?.screen_id ?? null,\n screenOrientation: data.data?.screen_orientation ?? null,\n screenDimensions: data.data?.screen_dimensions ?? null,\n etag: newEtag,\n notModified: false,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n }\n\n /**\n * Fire an impression pixel.\n *\n * Uses a GET request with query-string parameters so the call\n * survives `navigator.sendBeacon`-like fallback patterns and\n * can be retried transparently on network failure.\n *\n * Returns `true` if the server acknowledged the impression.\n */\n async reportImpression(impression: {\n adid: string;\n impid: string;\n did: string;\n aid: string;\n duration: number;\n completed: boolean;\n }): Promise<boolean> {\n try {\n const params = new URLSearchParams({\n adid: impression.adid,\n impid: impression.impid,\n did: impression.did,\n aid: impression.aid,\n duration: String(impression.duration || 0),\n completed: impression.completed ? 'true' : 'false',\n });\n const response = await fetch(`${this.apiBase}/impression?${params}`, {\n method: 'GET',\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Ping the heartbeat endpoint to signal this device is alive.\n * Returns `true` on 2xx, `false` on any error.\n */\n async sendHeartbeat(deviceId: string, screenId: string | null): Promise<boolean> {\n try {\n const response = await fetch(`${this.apiBase}/device/${deviceId}/heartbeat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n device_id: deviceId,\n screen_id: screenId,\n timestamp: new Date().toISOString(),\n status: 'active',\n }),\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Report a programmatic lifecycle event (VAST fill, timeout,\n * error, etc.) to the analytics backend.\n */\n async reportProgrammaticEvent(event: {\n screenId: string;\n deviceId: string;\n eventType: string;\n vastSource?: string;\n variantName?: string;\n errorCode?: number;\n errorMessage?: string;\n duration?: number;\n telemetry?: Record<string, number>;\n }): Promise<boolean> {\n try {\n const response = await fetch(`${this.apiBase}/programmatic-event`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n screen_id: event.screenId,\n device_id: event.deviceId,\n event_type: event.eventType,\n vast_source: event.vastSource,\n variant_name: event.variantName,\n error_code: event.errorCode,\n error_message: event.errorMessage,\n duration: event.duration,\n telemetry: event.telemetry,\n timestamp: new Date().toISOString(),\n }),\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — IndexedDB-backed ad cache\n//\n// Persists ads + pending impressions across page reloads so the\n// player can start immediately from cache while a fresh fetch\n// is in-flight, and impression tracking survives connectivity\n// gaps.\n// ─────────────────────────────────────────────────────────────\n\nimport type { AdItem } from '../core/types';\n\n/** Internal type for ads stored with a cache timestamp. */\ninterface CachedAdItem extends AdItem {\n cached_at: number;\n}\n\n/**\n * Two-store IndexedDB cache:\n *\n * - **ads** — most-recently-fetched ad creatives\n * - **pendingImpressions** — impression payloads queued while\n * offline or during transient failures\n *\n * Gracefully degrades to a no-op when `indexedDB` is unavailable\n * (SSR, Node.js, restricted iframes).\n */\nexport class IndexedDBCache {\n private db: IDBDatabase | null = null;\n private readonly DB_NAME = 'TrillboardsAdsCache';\n private readonly DB_VERSION = 1;\n private readonly STORE_ADS = 'ads';\n private readonly STORE_IMPRESSIONS = 'pendingImpressions';\n private maxCacheSize: number;\n\n constructor(maxCacheSize = 10) {\n this.maxCacheSize = maxCacheSize;\n }\n\n /**\n * Open (or create) the database. Must be called once before any\n * read/write operations — but calling it multiple times is safe.\n */\n async init(): Promise<void> {\n if (this.db) return; // Already initialized — idempotent guard\n return new Promise((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n resolve(); // No IndexedDB support (SSR, etc.)\n return;\n }\n const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.db = request.result;\n resolve();\n };\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(this.STORE_ADS)) {\n const adsStore = db.createObjectStore(this.STORE_ADS, { keyPath: 'id' });\n adsStore.createIndex('cached_at', 'cached_at', { unique: false });\n }\n if (!db.objectStoreNames.contains(this.STORE_IMPRESSIONS)) {\n db.createObjectStore(this.STORE_IMPRESSIONS, { keyPath: 'impid' });\n }\n };\n });\n }\n\n /**\n * Persist an array of ads into the cache, stamping each with the\n * current time. After writing, any ads beyond `maxCacheSize` are\n * evicted (oldest-first).\n */\n async cacheAds(ads: AdItem[]): Promise<void> {\n if (!this.db) return;\n const tx = this.db.transaction(this.STORE_ADS, 'readwrite');\n const store = tx.objectStore(this.STORE_ADS);\n const now = Date.now();\n for (const ad of ads) {\n store.put({ ...ad, cached_at: now });\n }\n // Wait for the put transaction to complete before evicting\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n await this.evictOldAds();\n }\n\n /**\n * Return every cached ad. The results may include the\n * `cached_at` bookkeeping field added during `cacheAds`.\n */\n async getCachedAds(): Promise<AdItem[]> {\n if (!this.db) return [];\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.STORE_ADS, 'readonly');\n const store = tx.objectStore(this.STORE_ADS);\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n }\n\n /**\n * Remove the oldest cached ads when the store exceeds\n * `maxCacheSize`. Sorts by `cached_at` descending and\n * deletes everything beyond the limit.\n */\n private async evictOldAds(): Promise<void> {\n if (!this.db) return;\n const ads = await this.getCachedAds() as CachedAdItem[];\n if (ads.length <= this.maxCacheSize) return;\n const sorted = [...ads].sort(\n (a, b) => (b.cached_at ?? 0) - (a.cached_at ?? 0),\n );\n const toRemove = sorted.slice(this.maxCacheSize);\n const tx = this.db.transaction(this.STORE_ADS, 'readwrite');\n const store = tx.objectStore(this.STORE_ADS);\n for (const ad of toRemove) {\n store.delete(ad.id);\n }\n }\n\n /**\n * Queue an impression payload for later delivery.\n * The object **must** contain an `impid` field (used as the\n * primary key).\n */\n async savePendingImpression(impression: Record<string, unknown>): Promise<void> {\n if (!this.db) return;\n const tx = this.db.transaction(this.STORE_IMPRESSIONS, 'readwrite');\n const store = tx.objectStore(this.STORE_IMPRESSIONS);\n store.put(impression);\n }\n\n /** Retrieve all queued impressions (FIFO order not guaranteed). */\n async getPendingImpressions(): Promise<Record<string, unknown>[]> {\n if (!this.db) return [];\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.STORE_IMPRESSIONS, 'readonly');\n const store = tx.objectStore(this.STORE_IMPRESSIONS);\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => resolve([]);\n });\n }\n\n /** Remove all pending impressions (typically after a successful flush). */\n async clearPendingImpressions(): Promise<void> {\n if (!this.db) return;\n const tx = this.db.transaction(this.STORE_IMPRESSIONS, 'readwrite');\n const store = tx.objectStore(this.STORE_IMPRESSIONS);\n store.clear();\n }\n\n /** Close the database connection and release the reference. */\n destroy(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Native platform bridge\n//\n// Auto-detects the host environment (Android WebView, WKWebView,\n// React Native, Flutter, CTV, Electron, Tauri, iframe) and\n// provides a unified send/receive interface for ad lifecycle\n// events flowing between the SDK and the native shell.\n// ─────────────────────────────────────────────────────────────\n\nimport type { BridgeConfig, BridgeType, BridgePayload } from '../core/types';\n\n// ── Global type augmentation ────────────────────────────────\n\ndeclare global {\n interface Window {\n TrillboardsBridge?: { onEvent: (payload: string) => void };\n Android?: { onTrillboardsEvent: (payload: string) => void };\n webkit?: {\n messageHandlers?: {\n trillboards?: { postMessage: (data: unknown) => void };\n };\n };\n ReactNativeWebView?: { postMessage: (message: string) => void };\n Flutter?: { postMessage: (message: string) => void };\n __TRILL_CTV_BRIDGE__?: { postEvent: (payload: string) => void };\n electronAPI?: { trillboardsEvent: (data: unknown) => void };\n __TAURI__?: { event: { emit: (event: string, data: unknown) => void } };\n }\n}\n\n// ── Internal helpers ────────────────────────────────────────\n\ninterface DetectedBridge {\n type: BridgeType;\n send: (payload: string) => void;\n}\n\ninterface RegisteredBridge {\n send: (event: string, data: unknown) => void;\n receive: ((callback: (command: unknown) => void) => void) | null;\n name: string;\n}\n\n// ── NativeBridge class ──────────────────────────────────────\n\n/**\n * Bidirectional bridge between the web-based ad player and its\n * native host application.\n *\n * **Outbound flow (SDK -> Native)**\n *\n * 1. The caller invokes `send(event, data)`.\n * 2. If a custom bridge was registered via `register()`, that\n * bridge's `send` callback is used first.\n * 3. Otherwise the bridge probes `window` for known native\n * injection points (see `detect()`).\n *\n * **Inbound flow (Native -> SDK)**\n *\n * - Native code posts a `MessageEvent` with\n * `{ type: 'trillboards-command', ... }` to `window`.\n * - `initCommandListener()` wires up the listener.\n * - The registered `commandHandler` is invoked with the payload.\n */\nexport class NativeBridge {\n readonly VERSION = '1.0';\n private registered: RegisteredBridge | null = null;\n private detected: DetectedBridge | null = null;\n readonly sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;\n private eventFilter: Set<string> | null = null;\n private commandHandler: ((command: unknown) => void) | null = null;\n private deviceId: string | null = null;\n private screenId: string | null = null;\n private messageListener: ((event: MessageEvent) => void) | null = null;\n private targetOrigin: string = '*';\n\n /**\n * Attach device / screen identifiers so every outgoing payload\n * carries them automatically.\n */\n setContext(deviceId: string | null, screenId: string | null) {\n this.deviceId = deviceId;\n this.screenId = screenId;\n }\n\n /**\n * Manually register a custom bridge (useful when the native\n * shell cannot inject a global but *can* pass callbacks at\n * construction time).\n *\n * Returns `true` if registration succeeded.\n */\n register(config: BridgeConfig): boolean {\n if (typeof config.send !== 'function') {\n console.error('[TrillboardsAds] Bridge requires send function');\n return false;\n }\n this.registered = {\n send: config.send,\n receive: config.receive ?? null,\n name: config.name ?? 'CustomBridge',\n };\n if (Array.isArray(config.events)) {\n this.eventFilter = new Set(config.events);\n }\n if (this.registered.receive && this.commandHandler) {\n this.registered.receive(this.commandHandler);\n }\n return true;\n }\n\n /**\n * Set the handler that will receive inbound commands from the\n * native shell (e.g. pause, resume, skip).\n */\n setCommandHandler(handler: (command: unknown) => void) {\n this.commandHandler = handler;\n }\n\n /**\n * Set the target origin for iframe postMessage bridge.\n * Defaults to `'*'` for backwards compatibility; should be\n * locked down in production to the parent origin.\n */\n setTargetOrigin(origin: string): void {\n this.targetOrigin = origin;\n }\n\n /**\n * Probe `window` for known native bridge injection points.\n *\n * Detection order:\n * 1. `window.TrillboardsBridge` -- Android (primary)\n * 2. `window.Android` -- Android (legacy)\n * 3. `window.webkit.messageHandlers.trillboards` -- iOS / WKWebView\n * 4. `window.ReactNativeWebView` -- React Native WebView\n * 5. `window.Flutter` -- Flutter WebView\n * 6. `window.__TRILL_CTV_BRIDGE__` -- Trillboards CTV agent\n * 7. `window.electronAPI` -- Electron preload\n * 8. `window.__TAURI__` -- Tauri IPC\n * 9. `window.parent !== window` -- iframe / postMessage\n *\n * Re-detects on every call (no caching) so dynamically\n * injected/removed bridges are picked up immediately.\n */\n detect(): DetectedBridge | null {\n if (typeof window === 'undefined') return null;\n\n // Re-detect every time — the cost of checking window properties\n // is negligible and bridges may be injected/removed dynamically\n let detected: DetectedBridge | null = null;\n\n if (window.TrillboardsBridge && typeof window.TrillboardsBridge.onEvent === 'function') {\n detected = {\n type: 'android',\n send: (p) => window.TrillboardsBridge!.onEvent(p),\n };\n } else if (window.Android && typeof window.Android.onTrillboardsEvent === 'function') {\n detected = {\n type: 'android-alt',\n send: (p) => window.Android!.onTrillboardsEvent(p),\n };\n } else if (window.webkit?.messageHandlers?.trillboards?.postMessage) {\n detected = {\n type: 'ios',\n send: (p) => window.webkit!.messageHandlers!.trillboards!.postMessage(JSON.parse(p)),\n };\n } else if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {\n detected = {\n type: 'react-native',\n send: (p) => window.ReactNativeWebView!.postMessage(p),\n };\n } else if (window.Flutter && typeof window.Flutter.postMessage === 'function') {\n detected = {\n type: 'flutter',\n send: (p) => window.Flutter!.postMessage(p),\n };\n } else if (window.__TRILL_CTV_BRIDGE__ && typeof window.__TRILL_CTV_BRIDGE__.postEvent === 'function') {\n detected = {\n type: 'ctv',\n send: (p) => window.__TRILL_CTV_BRIDGE__!.postEvent(p),\n };\n } else if (window.electronAPI && typeof window.electronAPI.trillboardsEvent === 'function') {\n detected = {\n type: 'electron',\n send: (p) => window.electronAPI!.trillboardsEvent(JSON.parse(p)),\n };\n } else if (window.__TAURI__?.event) {\n detected = {\n type: 'tauri',\n send: (p) => {\n try {\n window.__TAURI__!.event.emit('trillboards-event', JSON.parse(p));\n } catch {\n // Tauri emit may throw if the event channel is closed\n }\n },\n };\n } else if (\n typeof window !== 'undefined' &&\n window.parent !== window &&\n typeof window.parent.postMessage === 'function'\n ) {\n const origin = this.targetOrigin;\n detected = {\n type: 'postMessage',\n send: (p) => window.parent.postMessage(JSON.parse(p), origin),\n };\n }\n\n this.detected = detected;\n return this.detected;\n }\n\n /**\n * Serialise an event + data into the canonical Trillboards\n * bridge envelope.\n */\n buildPayload(event: string, data: unknown): string {\n const payload: BridgePayload = {\n type: 'trillboards',\n version: this.VERSION,\n event,\n data: data ?? {},\n timestamp: Date.now(),\n deviceId: this.deviceId,\n screenId: this.screenId,\n sessionId: this.sessionId,\n };\n return JSON.stringify(payload);\n }\n\n /**\n * Deliver an event to the native host.\n *\n * Returns `true` if a bridge accepted the payload, `false` if\n * the event was filtered out or no bridge is available.\n */\n send(event: string, data: unknown): boolean {\n if (this.eventFilter && !this.eventFilter.has(event)) return false;\n const payload = this.buildPayload(event, data);\n\n // Prefer the explicitly-registered bridge\n if (this.registered?.send) {\n try {\n this.registered.send(event, JSON.parse(payload));\n return true;\n } catch {\n // Fall through to auto-detected bridge\n }\n }\n\n // Fall back to auto-detected native bridge\n const detected = this.detect();\n if (detected?.send) {\n try {\n detected.send(payload);\n return true;\n } catch {\n // Bridge injection point exists but threw\n }\n }\n\n return false;\n }\n\n /**\n * Start listening for inbound `trillboards-command` messages\n * from the native shell (via `window.postMessage`).\n *\n * Call this once during SDK initialisation.\n */\n initCommandListener() {\n if (typeof window === 'undefined') return;\n // Remove previous listener if one exists (idempotent re-init)\n if (this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n }\n this.messageListener = (event: MessageEvent) => {\n if (event.data?.type === 'trillboards-command' && this.commandHandler) {\n this.commandHandler(event.data);\n }\n };\n window.addEventListener('message', this.messageListener);\n }\n\n /**\n * Remove the message listener and release references.\n * Call during SDK teardown to prevent memory leaks.\n */\n destroy(): void {\n if (typeof window !== 'undefined' && this.messageListener) {\n window.removeEventListener('message', this.messageListener);\n this.messageListener = null;\n }\n this.registered = null;\n this.detected = null;\n this.commandHandler = null;\n this.eventFilter = null;\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Per-source circuit breaker\n// ─────────────────────────────────────────────────────────────\n\nimport type { CircuitBreakerState } from '../core/types';\n\n/**\n * Per-source failure tracking to avoid hammering broken demand\n * sources. Each source independently transitions through three\n * states:\n *\n * CLOSED → failures < maxFailures → requests pass through\n * OPEN → failures >= maxFailures → requests blocked until openDurationMs elapses\n * HALF-OPEN → openDurationMs elapsed → one request allowed; success resets, failure re-opens\n */\nexport class CircuitBreaker {\n private sources: Map<string, CircuitBreakerState> = new Map();\n readonly maxFailures: number;\n readonly openDurationMs: number;\n\n constructor(maxFailures = 5, openDurationMs = 30000) {\n this.maxFailures = maxFailures;\n this.openDurationMs = openDurationMs;\n }\n\n /**\n * Record a successful request for a source, resetting its\n * failure counter and clearing any open-circuit timer.\n */\n recordSuccess(sourceName: string): void {\n if (!sourceName) return;\n this.sources.set(sourceName, { consecutiveFailures: 0, openUntil: 0 });\n }\n\n /**\n * Record a failed request for a source. When consecutive\n * failures reach `maxFailures`, the circuit opens for\n * `openDurationMs` milliseconds.\n */\n recordFailure(sourceName: string): void {\n if (!sourceName) return;\n const entry = this.sources.get(sourceName) ?? { consecutiveFailures: 0, openUntil: 0 };\n entry.consecutiveFailures += 1;\n if (entry.consecutiveFailures >= this.maxFailures) {\n entry.openUntil = Date.now() + this.openDurationMs;\n }\n this.sources.set(sourceName, entry);\n }\n\n /**\n * Check whether a source is available for requests.\n *\n * Returns `true` when:\n * - The source has never been tracked (unknown = available)\n * - Consecutive failures are below the threshold (CLOSED)\n * - The open-circuit timer has elapsed (HALF-OPEN — allow a probe)\n */\n isAvailable(sourceName: string): boolean {\n if (!sourceName) return true;\n const entry = this.sources.get(sourceName);\n if (!entry) return true;\n if (entry.consecutiveFailures < this.maxFailures) return true;\n const now = Date.now();\n if (now >= entry.openUntil) {\n // Transition to half-open: reset openUntil so only one probe\n // gets through. If the probe succeeds, recordSuccess() fully\n // resets the breaker. If it fails, recordFailure() re-opens\n // with a fresh openDurationMs window.\n entry.openUntil = now + this.openDurationMs;\n return true;\n }\n return false;\n }\n\n /**\n * Return the raw circuit breaker state for a source.\n * Returns a zeroed-out state for unknown sources.\n */\n getState(sourceName: string): CircuitBreakerState {\n return this.sources.get(sourceName) ?? { consecutiveFailures: 0, openUntil: 0 };\n }\n\n /**\n * Clear all tracked source state.\n */\n reset(): void {\n this.sources.clear();\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Aggregate fill/error/timeout tracking\n// ─────────────────────────────────────────────────────────────\n\nimport type { TelemetrySnapshot } from '../core/types';\n\n/**\n * Lightweight counters for ad-request outcomes.\n *\n * Every call to `play()` on the programmatic player should call\n * `recordRequest()`. Depending on the outcome, one of\n * `recordFill()`, `recordNoFill()`, `recordTimeout()`, or\n * `recordError()` is then called. The snapshot exposes rates\n * (0-1, rounded to 4 decimal places) that can be sent to the\n * server for monitoring.\n */\nexport class Telemetry {\n private totalRequests = 0;\n private fills = 0;\n private noFills = 0;\n private timeouts = 0;\n private errors = 0;\n\n /** Count a new ad request. */\n recordRequest(): void {\n this.totalRequests++;\n }\n\n /** Count a successful fill (ad loaded and ready to play). */\n recordFill(): void {\n this.fills++;\n }\n\n /** Count a request that returned no ad (no fill). */\n recordNoFill(): void {\n this.noFills++;\n }\n\n /** Count a request that timed out before a response arrived. */\n recordTimeout(): void {\n this.timeouts++;\n }\n\n /** Count a request that failed with an error (not a timeout). */\n recordError(): void {\n this.errors++;\n }\n\n /** Ratio of fills to total requests (0-1). */\n getFillRate(): number {\n return this.totalRequests > 0 ? this.fills / this.totalRequests : 0;\n }\n\n /** Ratio of no-fills to total requests (0-1). */\n getNoFillRate(): number {\n return this.totalRequests > 0 ? this.noFills / this.totalRequests : 0;\n }\n\n /** Ratio of timeouts to total requests (0-1). */\n getTimeoutRate(): number {\n return this.totalRequests > 0 ? this.timeouts / this.totalRequests : 0;\n }\n\n /** Ratio of errors to total requests (0-1). */\n getErrorRate(): number {\n return this.totalRequests > 0 ? this.errors / this.totalRequests : 0;\n }\n\n /**\n * Return a snapshot object suitable for serialization.\n * Rates are rounded to four decimal places.\n */\n getSnapshot(): TelemetrySnapshot {\n return {\n fill_rate: Math.round(this.getFillRate() * 10000) / 10000,\n no_fill_rate: Math.round(this.getNoFillRate() * 10000) / 10000,\n timeout_rate: Math.round(this.getTimeoutRate() * 10000) / 10000,\n error_rate: Math.round(this.getErrorRate() * 10000) / 10000,\n };\n }\n\n /** Reset all counters to zero. */\n reset(): void {\n this.totalRequests = 0;\n this.fills = 0;\n this.noFills = 0;\n this.timeouts = 0;\n this.errors = 0;\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Waterfall demand-source engine\n// ─────────────────────────────────────────────────────────────\n\nimport type { VastSource } from '../core/types';\nimport { CircuitBreaker } from './CircuitBreaker';\n\nexport interface WaterfallResult {\n source: VastSource;\n vastUrl: string;\n}\n\n/**\n * Manages iterating through waterfall demand sources in priority\n * order, skipping circuit-broken sources. Lower priority numbers\n * are tried first. Equal-priority sources are randomized to\n * distribute load evenly.\n */\nexport class WaterfallEngine {\n private circuitBreaker: CircuitBreaker;\n\n constructor(circuitBreaker: CircuitBreaker) {\n this.circuitBreaker = circuitBreaker;\n }\n\n /**\n * Get the next available VAST source from the waterfall.\n * Sources are sorted by priority (lower = higher priority),\n * with a random tiebreaker for equal priorities.\n * Circuit-broken sources are skipped.\n *\n * If all sources are circuit-broken, returns `null` to let\n * the caller handle retry scheduling rather than bypassing\n * the circuit breaker.\n */\n getNextSource(sources: VastSource[]): WaterfallResult | null {\n if (!sources || sources.length === 0) return null;\n\n const sorted = [...sources]\n .filter(s => s.enabled)\n .sort((a, b) => a.priority - b.priority || Math.random() - 0.5);\n\n for (const source of sorted) {\n if (this.circuitBreaker.isAvailable(source.name)) {\n return { source, vastUrl: source.vast_url };\n }\n }\n\n // All sources circuit-broken — return null so the caller can\n // schedule a retry instead of bypassing circuit protection\n return null;\n }\n\n /**\n * Get all available sources in priority order (for parallel bidding).\n * Only returns sources that are not circuit-broken.\n * Equal-priority sources are randomized for load distribution.\n */\n getAvailableSources(sources: VastSource[]): WaterfallResult[] {\n if (!sources || sources.length === 0) return [];\n\n return sources\n .filter(s => s.enabled && this.circuitBreaker.isAvailable(s.name))\n .sort((a, b) => a.priority - b.priority || Math.random() - 0.5)\n .map(source => ({ source, vastUrl: source.vast_url }));\n }\n\n /** Record a successful ad fill for a source. */\n recordSuccess(sourceName: string): void {\n this.circuitBreaker.recordSuccess(sourceName);\n }\n\n /** Record a failed request for a source. */\n recordFailure(sourceName: string): void {\n this.circuitBreaker.recordFailure(sourceName);\n }\n}\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Programmatic (VAST / IMA) ad player\n// ─────────────────────────────────────────────────────────────\n\nimport type { VastSource, TelemetrySnapshot } from '../core/types';\nimport { CircuitBreaker } from './CircuitBreaker';\nimport { Telemetry } from './Telemetry';\nimport { WaterfallEngine } from './WaterfallEngine';\nimport { EventEmitter } from '../core/events';\n\ninterface IMAObjects {\n adDisplayContainer?: any;\n adsLoader?: any;\n adsManager?: any;\n adsRequest?: any;\n}\n\n/**\n * Manages VAST/IMA ad playback. Uses the Google IMA SDK when\n * available to request, render, and track programmatic video\n * ads inside an overlay container.\n *\n * Integrates CircuitBreaker + WaterfallEngine + Telemetry for\n * resilient multi-source demand.\n */\nexport class ProgrammaticPlayer {\n // ── Public state ──────────────────────────────────────────\n vastTagUrl: string | null = null;\n variantName: string | null = null;\n screenId: string | null = null;\n adContainer: HTMLDivElement | null = null;\n contentVideo: HTMLVideoElement | null = null;\n\n // ── Internal state ────────────────────────────────────────\n private ima: IMAObjects = {};\n private currentSourceName: string | null = null;\n private isPlaying = false;\n private isPrefetching = false;\n private prefetchedReady = false;\n private prefetchTimer: ReturnType<typeof setTimeout> | null = null;\n private adStartTime = 0;\n\n // ── Modules ───────────────────────────────────────────────\n private circuitBreaker: CircuitBreaker;\n private telemetry: Telemetry;\n private waterfallEngine: WaterfallEngine;\n private events: EventEmitter;\n\n // ── Waterfall sources from server ─────────────────────────\n private sources: VastSource[] = [];\n private timeoutMs: number;\n\n constructor(events: EventEmitter, timeoutMs = 12000) {\n this.circuitBreaker = new CircuitBreaker(5, 30000);\n this.telemetry = new Telemetry();\n this.waterfallEngine = new WaterfallEngine(this.circuitBreaker);\n this.events = events;\n this.timeoutMs = timeoutMs;\n }\n\n /** Replace the current set of demand sources. */\n setSources(sources: VastSource[]): void {\n this.sources = sources;\n }\n\n /** Return aggregate fill/timeout/error rates. */\n getTelemetrySnapshot(): TelemetrySnapshot {\n return this.telemetry.getSnapshot();\n }\n\n /** Whether a programmatic ad is currently playing. */\n getIsPlaying(): boolean {\n return this.isPlaying;\n }\n\n /** Whether a prefetched ad is ready for instant playback. */\n hasPrefetchedAd(): boolean {\n return this.prefetchedReady;\n }\n\n /**\n * Create the ad container and content video element needed\n * by IMA SDK. Appended as an overlay to `parentElement`.\n */\n createAdContainer(parentElement: HTMLElement): void {\n if (this.adContainer) return;\n\n this.adContainer = document.createElement('div');\n this.adContainer.id = 'trillboards-ad-container';\n this.adContainer.style.cssText =\n 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:10000;display:none;';\n\n this.contentVideo = document.createElement('video');\n this.contentVideo.id = 'trillboards-content-video';\n this.contentVideo.style.cssText = 'width:100%;height:100%;';\n this.contentVideo.setAttribute('playsinline', '');\n this.contentVideo.muted = true;\n\n this.adContainer.appendChild(this.contentVideo);\n parentElement.appendChild(this.adContainer);\n }\n\n /**\n * Request and play a VAST ad using Google IMA SDK.\n *\n * Picks the next available source from the waterfall (or\n * falls back to `vastTagUrl`), adds a cache-busting correlator,\n * and delegates to IMA for ad loading + playback.\n */\n async play(\n onComplete: () => void,\n onError: (error: string) => void,\n ): Promise<void> {\n if (this.isPlaying) return;\n if (!this.adContainer || !this.contentVideo) {\n onError('Ad container not initialized');\n return;\n }\n\n // Determine VAST URL: use waterfall sources if available, else direct URL\n let vastUrl = this.vastTagUrl;\n let sourceName = 'default';\n\n if (this.sources.length > 0) {\n const result = this.waterfallEngine.getNextSource(this.sources);\n if (result) {\n vastUrl = result.vastUrl;\n sourceName = result.source.name;\n }\n }\n\n if (!vastUrl) {\n onError('No VAST URL available');\n return;\n }\n\n this.currentSourceName = sourceName;\n this.telemetry.recordRequest();\n\n // Add correlator to bust caching\n const correlator = Date.now();\n const finalUrl = vastUrl.includes('correlator=')\n ? vastUrl.replace(/correlator=[^&]*/, `correlator=${correlator}`)\n : `${vastUrl}${vastUrl.includes('?') ? '&' : '?'}correlator=${correlator}`;\n\n // Check for Google IMA SDK\n if (typeof google === 'undefined' || !google?.ima) {\n onError('Google IMA SDK not loaded');\n this.telemetry.recordError();\n this.waterfallEngine.recordFailure(sourceName);\n return;\n }\n\n this.isPlaying = true;\n this.adContainer.style.display = 'block';\n\n try {\n await this.requestAdsViaIMA(finalUrl, onComplete, onError);\n } catch (err) {\n this.isPlaying = false;\n this.adContainer.style.display = 'none';\n this.telemetry.recordError();\n this.waterfallEngine.recordFailure(sourceName);\n onError(err instanceof Error ? err.message : String(err));\n }\n }\n\n // ── IMA request / playback lifecycle ──────────────────────\n\n private async requestAdsViaIMA(\n vastUrl: string,\n onComplete: () => void,\n onError: (error: string) => void,\n ): Promise<void> {\n return new Promise<void>((resolve) => {\n // Double-callback guard — ensures onComplete/onError are\n // called at most once across all 5 callback paths (timeout,\n // AD_ERROR on manager, AD_ERROR on loader, start() catch, COMPLETE)\n let settled = false;\n const guardedComplete = () => {\n if (settled) return;\n settled = true;\n onComplete();\n resolve();\n };\n const guardedError = (msg: string) => {\n if (settled) return;\n settled = true;\n onError(msg);\n resolve();\n };\n\n // Clean up previous IMA session\n this.destroyIMA();\n\n const adDisplayContainer = new google.ima.AdDisplayContainer(\n this.adContainer!,\n this.contentVideo!,\n );\n adDisplayContainer.initialize();\n\n const adsLoader = new google.ima.AdsLoader(adDisplayContainer);\n this.ima.adDisplayContainer = adDisplayContainer;\n this.ima.adsLoader = adsLoader;\n\n // Timeout guard — if IMA doesn't respond in time, bail out\n const timeout = setTimeout(() => {\n this.telemetry.recordTimeout();\n this.waterfallEngine.recordFailure(this.currentSourceName!);\n this.stop({ silent: true });\n this.events.emit('programmatic_timeout', {\n source: this.currentSourceName || 'unknown',\n });\n guardedError('VAST request timeout');\n }, this.timeoutMs);\n\n // ── ADS_MANAGER_LOADED: ad fill received ──────────────\n adsLoader.addEventListener(\n google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,\n (adsManagerEvent: any) => {\n clearTimeout(timeout);\n this.telemetry.recordFill();\n this.waterfallEngine.recordSuccess(this.currentSourceName!);\n\n const adsManager = adsManagerEvent.getAdsManager(this.contentVideo!);\n this.ima.adsManager = adsManager;\n this.adStartTime = Date.now();\n\n // Ad completed — clean up and signal caller\n adsManager.addEventListener(\n google.ima.AdEvent.Type.COMPLETE,\n () => {\n const duration = Math.round(\n (Date.now() - this.adStartTime) / 1000,\n );\n this.events.emit('programmatic_ended', {\n source: this.currentSourceName || 'unknown',\n variant: this.variantName,\n duration,\n });\n this.stop({ silent: true });\n guardedComplete();\n },\n );\n\n // Ad started — emit telemetry event\n adsManager.addEventListener(\n google.ima.AdEvent.Type.STARTED,\n () => {\n this.events.emit('programmatic_started', {\n source: this.currentSourceName || 'unknown',\n variant: this.variantName,\n });\n },\n );\n\n // Ad playback error\n adsManager.addEventListener(\n google.ima.AdErrorEvent.Type.AD_ERROR,\n (adErrorEvent: any) => {\n const error = adErrorEvent.getError();\n this.events.emit('programmatic_error', {\n error: error?.getMessage?.() || 'Ad playback error',\n code: error?.getErrorCode?.(),\n });\n this.stop({ silent: true });\n guardedError(error?.getMessage?.() || 'Ad error');\n },\n );\n\n // Initialize and start playback\n try {\n const width =\n this.adContainer!.offsetWidth || window.innerWidth;\n const height =\n this.adContainer!.offsetHeight || window.innerHeight;\n adsManager.init(width, height, google.ima.ViewMode.NORMAL);\n adsManager.start();\n } catch {\n this.stop({ silent: true });\n guardedError('Failed to start ad');\n }\n },\n );\n\n // ── AD_ERROR on the loader (request-level error) ──────\n adsLoader.addEventListener(\n google.ima.AdErrorEvent.Type.AD_ERROR,\n (adErrorEvent: any) => {\n clearTimeout(timeout);\n const error = adErrorEvent.getError();\n const errorCode = error?.getErrorCode?.();\n const errorMsg = error?.getMessage?.() || 'Ad load error';\n\n // 303 = \"No Ads VAST response\" — a no-fill, not an error\n if (errorCode === 303) {\n this.waterfallEngine.recordFailure(this.currentSourceName!);\n this.events.emit('programmatic_no_fill', {\n source: this.currentSourceName || 'unknown',\n });\n } else {\n this.telemetry.recordError();\n this.waterfallEngine.recordFailure(this.currentSourceName!);\n this.events.emit('programmatic_error', {\n error: errorMsg,\n code: errorCode,\n });\n }\n\n this.stop({ silent: true });\n guardedError(errorMsg);\n },\n );\n\n // ── Build and fire the ad request ─────────────────────\n const adsRequest = new google.ima.AdsRequest();\n adsRequest.adTagUrl = vastUrl;\n adsRequest.linearAdSlotWidth =\n this.adContainer!.offsetWidth || window.innerWidth;\n adsRequest.linearAdSlotHeight =\n this.adContainer!.offsetHeight || window.innerHeight;\n this.ima.adsRequest = adsRequest;\n\n adsLoader.requestAds(adsRequest);\n });\n }\n\n // ── Prefetch ──────────────────────────────────────────────\n\n /**\n * Prefetch the next ad for instant playback.\n * Currently marks prefetch as ready; actual IMA preloading\n * is a future enhancement.\n */\n async prefetch(): Promise<boolean> {\n if (this.isPrefetching || this.prefetchedReady) return false;\n if (!this.vastTagUrl && this.sources.length === 0) return false;\n\n this.isPrefetching = true;\n // Mark ready — actual preloading happens on play()\n this.prefetchedReady = true;\n this.isPrefetching = false;\n this.events.emit('ad_ready', {\n type: 'programmatic',\n source: this.currentSourceName || undefined,\n });\n return true;\n }\n\n /** Destroy any prefetched ad state. */\n destroyPrefetched(): void {\n this.prefetchedReady = false;\n this.isPrefetching = false;\n if (this.prefetchTimer) {\n clearTimeout(this.prefetchTimer);\n this.prefetchTimer = null;\n }\n }\n\n // ── Stop / destroy ────────────────────────────────────────\n\n /** Stop current ad playback and hide the container. */\n stop(_options?: { silent?: boolean }): void {\n this.isPlaying = false;\n this.destroyIMA();\n if (this.adContainer) {\n this.adContainer.style.display = 'none';\n }\n }\n\n private destroyIMA(): void {\n if (this.ima.adsManager) {\n try {\n // Remove event listeners BEFORE destroying to prevent ghost\n // events from firing during teardown\n this.ima.adsManager.removeEventListener(\n google.ima.AdEvent.Type.COMPLETE,\n );\n this.ima.adsManager.removeEventListener(\n google.ima.AdEvent.Type.STARTED,\n );\n this.ima.adsManager.removeEventListener(\n google.ima.AdErrorEvent.Type.AD_ERROR,\n );\n } catch {\n // google.ima may be undefined or listeners already removed\n }\n try {\n this.ima.adsManager.destroy();\n } catch {\n // IMA may throw if already destroyed\n }\n this.ima.adsManager = null;\n }\n if (this.ima.adsLoader) {\n try {\n this.ima.adsLoader.destroy();\n } catch {\n // IMA may throw if already destroyed\n }\n this.ima.adsLoader = null;\n }\n this.ima.adDisplayContainer = null;\n this.ima.adsRequest = null;\n }\n\n /** Tear down everything — DOM, timers, circuit breaker state. */\n destroy(): void {\n this.stop({ silent: true });\n this.destroyPrefetched();\n if (this.adContainer) {\n this.adContainer.remove();\n this.adContainer = null;\n this.contentVideo = null;\n }\n this.circuitBreaker.reset();\n this.telemetry.reset();\n }\n}\n\n// ── Type declarations for Google IMA SDK ──────────────────────\ndeclare const google: {\n ima: {\n AdDisplayContainer: new (\n container: HTMLElement,\n video: HTMLVideoElement,\n ) => any;\n AdsLoader: new (container: any) => any;\n AdsRequest: new () => any;\n AdsManagerLoadedEvent: {\n Type: { ADS_MANAGER_LOADED: string };\n };\n AdEvent: {\n Type: {\n COMPLETE: string;\n STARTED: string;\n ALL_ADS_COMPLETED: string;\n };\n };\n AdErrorEvent: { Type: { AD_ERROR: string } };\n ViewMode: { NORMAL: number };\n };\n};\n","// ─────────────────────────────────────────────────────────────\n// @trillboards/ads-sdk — Direct ad player (image / video)\n// ─────────────────────────────────────────────────────────────\n\nimport type { AdItem } from '../core/types';\nimport { EventEmitter } from '../core/events';\n\n/**\n * Plays non-programmatic (direct) ads — static images shown for\n * a configurable duration and videos played to completion.\n *\n * Renders into a caller-provided container element by creating\n * and managing the appropriate `<img>` or `<video>` DOM nodes.\n */\n/** Maximum allowed ad duration in seconds to guard against unreasonable values. */\nconst MAX_AD_DURATION_SECONDS = 300;\n\nexport class Player {\n private container: HTMLElement | null = null;\n private currentAd: AdItem | null = null;\n private adTimer: ReturnType<typeof setTimeout> | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private imageElement: HTMLImageElement | null = null;\n private events: EventEmitter;\n private defaultImageDuration: number;\n private videoEndedHandler: (() => void) | null = null;\n private videoErrorHandler: (() => void) | null = null;\n\n /**\n * @param events Shared event emitter for SDK-wide events.\n * @param defaultImageDuration Fallback display time for images in ms.\n */\n constructor(events: EventEmitter, defaultImageDuration = 8000) {\n this.events = events;\n this.defaultImageDuration = defaultImageDuration;\n }\n\n /** Set the DOM element that ads will be rendered into. */\n setContainer(container: HTMLElement): void {\n this.container = container;\n }\n\n /**\n * Play a direct ad. Stops any currently playing ad first.\n * Calls `onComplete` when playback finishes (natural end or\n * error fallback).\n *\n * @param ad The ad to play.\n * @param onComplete Called when the ad finishes or errors.\n * @param index Position in the playlist (emitted in ad_started).\n */\n play(ad: AdItem, onComplete: () => void, index = 0): void {\n if (!this.container) return;\n this.stop();\n this.currentAd = ad;\n\n this.events.emit('ad_started', { id: ad.id, type: ad.type, index });\n\n if (ad.type === 'video') {\n this.playVideo(ad, onComplete);\n } else {\n this.playImage(ad, onComplete);\n }\n }\n\n // ── Video playback ────────────────────────────────────────\n\n private playVideo(ad: AdItem, onComplete: () => void): void {\n this.videoElement = document.createElement('video');\n this.videoElement.src = ad.media_url;\n this.videoElement.style.cssText =\n 'width:100%;height:100%;object-fit:contain;';\n this.videoElement.setAttribute('playsinline', '');\n this.videoElement.muted = true;\n this.videoElement.autoplay = true;\n\n this.videoEndedHandler = () => {\n // ad.duration is in seconds; cap at MAX_AD_DURATION_SECONDS\n const duration = Math.min(\n Math.round(this.videoElement?.duration ?? ad.duration),\n MAX_AD_DURATION_SECONDS,\n );\n this.events.emit('ad_ended', {\n id: ad.id,\n type: ad.type,\n duration,\n });\n this.stop();\n onComplete();\n };\n\n this.videoErrorHandler = () => {\n this.events.emit('ad_error', { error: 'Video playback error' });\n this.stop();\n onComplete();\n };\n\n this.videoElement.addEventListener('ended', this.videoEndedHandler);\n this.videoElement.addEventListener('error', this.videoErrorHandler);\n\n // Remove existing children without using innerHTML (preserves event listeners)\n while (this.container!.firstChild) {\n this.container!.removeChild(this.container!.firstChild);\n }\n this.container!.appendChild(this.videoElement);\n }\n\n // ── Image playback ────────────────────────────────────────\n\n private playImage(ad: AdItem, onComplete: () => void): void {\n this.imageElement = document.createElement('img');\n this.imageElement.src = ad.media_url;\n this.imageElement.style.cssText =\n 'width:100%;height:100%;object-fit:contain;';\n this.imageElement.alt = ad.title ?? 'Advertisement';\n\n // Remove existing children without using innerHTML\n while (this.container!.firstChild) {\n this.container!.removeChild(this.container!.firstChild);\n }\n this.container!.appendChild(this.imageElement);\n\n // ad.duration is in seconds; cap at MAX_AD_DURATION_SECONDS\n const durationSec = Math.min(\n ad.duration || this.defaultImageDuration / 1000,\n MAX_AD_DURATION_SECONDS,\n );\n const durationMs = durationSec * 1000;\n this.adTimer = setTimeout(() => {\n this.events.emit('ad_ended', {\n id: ad.id,\n type: ad.type,\n duration: Math.round(durationSec),\n });\n this.stop();\n onComplete();\n }, durationMs);\n }\n\n // ── Stop / destroy ────────────────────────────────────────\n\n /** Stop any currently playing ad and clean up DOM nodes. */\n stop(): void {\n if (this.adTimer) {\n clearTimeout(this.adTimer);\n this.adTimer = null;\n }\n if (this.videoElement) {\n // Remove event listeners before teardown to prevent ghost callbacks\n if (this.videoEndedHandler) {\n this.videoElement.removeEventListener('ended', this.videoEndedHandler);\n this.videoEndedHandler = null;\n }\n if (this.videoErrorHandler) {\n this.videoElement.removeEventListener('error', this.videoErrorHandler);\n this.videoErrorHandler = null;\n }\n this.videoElement.pause();\n // Use removeAttribute('src') + load() instead of setting src = ''\n // to properly abort any in-progress network requests\n this.videoElement.removeAttribute('src');\n this.videoElement.load();\n this.videoElement.remove();\n this.videoElement = null;\n }\n if (this.imageElement) {\n this.imageElement.remove();\n this.imageElement = null;\n }\n this.currentAd = null;\n }\n\n /** Tear down the player, releasing the container reference. */\n destroy(): void {\n this.stop();\n this.container = null;\n }\n}\n","import type {\n TrillboardsConfig,\n TrillboardsState,\n AdItem,\n EventMap,\n BridgeConfig,\n WaterfallMode,\n ProgrammaticSettings,\n ScreenDimensions,\n} from './types';\nimport { resolveConfig, type ResolvedConfig } from './config';\nimport { createInitialState, getPublicState, type InternalState } from './state';\nimport { EventEmitter } from './events';\nimport { ApiClient } from '../api/client';\nimport { IndexedDBCache } from '../cache/IndexedDBCache';\nimport { NativeBridge } from '../bridge/NativeBridge';\nimport { ProgrammaticPlayer } from '../player/ProgrammaticPlayer';\nimport { Player } from '../player/Player';\n\nfunction normalizeWaterfallMode(value: string | undefined | null): WaterfallMode | null {\n if (!value) return null;\n const normalized = String(value).toLowerCase();\n if (['programmatic_only', 'programmatic-only', 'programmatic'].includes(normalized)) {\n return 'programmatic_only';\n }\n if (['programmatic_then_direct', 'programmatic-direct', 'programmatic+direct', 'hybrid'].includes(normalized)) {\n return 'programmatic_then_direct';\n }\n if (['direct_only', 'direct-only', 'direct'].includes(normalized)) {\n return 'direct_only';\n }\n return null;\n}\n\nexport class TrillboardsAds {\n /** Singleton reference — `create()` destroys any previous instance. */\n private static _instance: TrillboardsAds | null = null;\n\n private config: ResolvedConfig;\n private state: InternalState;\n private events: EventEmitter;\n private api: ApiClient;\n private cache: IndexedDBCache;\n private bridge: NativeBridge;\n private programmaticPlayer: ProgrammaticPlayer;\n private directPlayer: Player;\n\n /** Stored references for window event listeners (cleanup in destroy). */\n private onlineHandler: (() => void) | null = null;\n private offlineHandler: (() => void) | null = null;\n private visibilityHandler: (() => void) | null = null;\n\n private constructor(config: TrillboardsConfig) {\n this.config = resolveConfig(config);\n this.state = createInitialState();\n this.events = new EventEmitter();\n this.api = new ApiClient(this.config.apiBase);\n this.cache = new IndexedDBCache(this.config.cacheSize);\n this.bridge = new NativeBridge();\n this.programmaticPlayer = new ProgrammaticPlayer(this.events, this.config.programmaticTimeout);\n this.directPlayer = new Player(this.events, this.config.defaultImageDuration);\n }\n\n /**\n * Create and initialize a new TrillboardsAds instance.\n * If a previous instance exists, it is destroyed first\n * (singleton guard).\n */\n static async create(config: TrillboardsConfig): Promise<TrillboardsAds> {\n if (TrillboardsAds._instance) {\n TrillboardsAds._instance.destroy();\n TrillboardsAds._instance = null;\n }\n const instance = new TrillboardsAds(config);\n await instance.init(config);\n TrillboardsAds._instance = instance;\n return instance;\n }\n\n /**\n * Initialize the SDK (private — called by `create()` only).\n */\n private async init(config: TrillboardsConfig): Promise<void> {\n if (this.state.initialized) return;\n\n this.config = resolveConfig(config);\n this.state.deviceId = this.config.deviceId;\n this.state.waterfallMode = this.config.waterfall as WaterfallMode;\n this.state.autoStart = this.config.autoStart;\n\n // Initialize cache\n try {\n await this.cache.init();\n } catch {\n // Cache init failure is non-fatal\n }\n\n // Set up native bridge\n this.bridge.setContext(this.state.deviceId, this.state.screenId);\n this.bridge.setCommandHandler((command) => this.handleBridgeCommand(command));\n this.bridge.initCommandListener();\n this.bridge.detect();\n\n // Forward events to bridge\n const bridgeEvents: (keyof EventMap)[] = [\n 'initialized', 'ad_started', 'ad_ended', 'ad_ready', 'ad_error',\n 'programmatic_started', 'programmatic_ended', 'programmatic_error',\n 'programmatic_no_fill', 'state_changed',\n ];\n for (const eventName of bridgeEvents) {\n this.events.on(eventName, (data: unknown) => {\n this.bridge.send(eventName, data);\n });\n }\n\n // Create container\n this.createContainer();\n\n // Add online/offline listeners\n if (typeof window !== 'undefined') {\n this.onlineHandler = () => {\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.events.emit('online', {} as Record<string, never>);\n }\n this.refreshAds().catch(() => {});\n };\n this.offlineHandler = () => {\n this.state.isOffline = true;\n this.events.emit('offline', {} as Record<string, never>);\n this.emitStateChanged();\n };\n window.addEventListener('online', this.onlineHandler);\n window.addEventListener('offline', this.offlineHandler);\n\n // Add visibility change listener\n this.visibilityHandler = () => {\n const visible = !document.hidden;\n this.events.emit('visibility_changed', { visible });\n if (visible) {\n this.refreshAds().catch(() => {});\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n\n // Fetch initial ads\n await this.refreshAds();\n\n // Start timers\n this.startRefreshTimer();\n this.startHeartbeatTimer();\n\n this.state.initialized = true;\n this.events.emit('initialized', { deviceId: this.config.deviceId });\n this.emitStateChanged();\n\n // Auto-start if configured\n if (this.state.autoStart) {\n this.prefetchNextAd().catch(() => {});\n }\n }\n\n /**\n * Destroy the SDK instance and clean up all resources.\n */\n destroy(): void {\n if (this.state.refreshTimer) clearInterval(this.state.refreshTimer);\n if (this.state.heartbeatTimer) clearInterval(this.state.heartbeatTimer);\n if (this.state.adTimer) clearTimeout(this.state.adTimer);\n if (this.state.programmaticRetryTimer) clearTimeout(this.state.programmaticRetryTimer);\n\n this.programmaticPlayer.destroy();\n this.directPlayer.destroy();\n this.bridge.destroy();\n this.cache.destroy();\n\n // Remove window event listeners\n if (typeof window !== 'undefined') {\n if (this.onlineHandler) {\n window.removeEventListener('online', this.onlineHandler);\n this.onlineHandler = null;\n }\n if (this.offlineHandler) {\n window.removeEventListener('offline', this.offlineHandler);\n this.offlineHandler = null;\n }\n if (this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n }\n\n if (this.state.container) {\n this.state.container.remove();\n }\n\n this.events.removeAllListeners();\n this.state = createInitialState();\n\n // Clear singleton reference if this is the current instance\n if (TrillboardsAds._instance === this) {\n TrillboardsAds._instance = null;\n }\n }\n\n /**\n * Show the ad container and start playback.\n */\n show(): void {\n if (this.state.container) {\n this.state.container.style.display = 'block';\n }\n this.state.isPlaying = true;\n this.state.isPaused = false;\n this.playNextAd();\n this.emitStateChanged();\n }\n\n /**\n * Hide the ad container and pause playback.\n */\n hide(): void {\n if (this.state.container) {\n this.state.container.style.display = 'none';\n }\n this.state.isPlaying = false;\n this.state.isPaused = true;\n this.programmaticPlayer.stop();\n this.directPlayer.stop();\n this.emitStateChanged();\n }\n\n /**\n * Skip the current ad.\n */\n skipAd(): void {\n this.programmaticPlayer.stop();\n this.directPlayer.stop();\n this.playNextAd();\n }\n\n /**\n * Force refresh — re-fetches VAST URLs from server with null etag.\n */\n async refresh(): Promise<void> {\n this.programmaticPlayer.destroyPrefetched();\n this.resetProgrammaticBackoff();\n this.state.etag = null;\n await this.refreshAds();\n this.prefetchNextAd();\n }\n\n /**\n * Prefetch the next ad for instant playback.\n */\n async prefetchNextAd(): Promise<void> {\n const mode = this.state.waterfallMode;\n if (mode === 'programmatic_only' || mode === 'programmatic_then_direct') {\n const prog = this.state.programmatic;\n if (prog?.vast_tag_url || (prog?.sources && prog.sources.length > 0)) {\n await this.programmaticPlayer.prefetch();\n }\n }\n }\n\n /**\n * Get current public state.\n */\n getState(): TrillboardsState {\n return getPublicState(this.state);\n }\n\n /**\n * Get current ad list.\n */\n getAds(): AdItem[] {\n return [...this.state.ads];\n }\n\n /**\n * Subscribe to an event.\n */\n on<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void): void {\n this.events.on(event, handler);\n }\n\n /**\n * Unsubscribe from an event.\n */\n off<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void): void {\n this.events.off(event, handler);\n }\n\n /**\n * Register a custom native bridge.\n */\n registerBridge(config: BridgeConfig): boolean {\n return this.bridge.register(config);\n }\n\n // ========================\n // Private methods\n // ========================\n\n private createContainer(): void {\n if (typeof document === 'undefined') return;\n\n const container = document.createElement('div');\n container.id = 'trillboards-ads-container';\n container.style.cssText =\n 'position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999;display:none;background:#000;';\n document.body.appendChild(container);\n this.state.container = container;\n\n this.api.setContainer(container);\n this.directPlayer.setContainer(container);\n this.programmaticPlayer.createAdContainer(container);\n }\n\n private async refreshAds(): Promise<void> {\n try {\n const result = await this.api.fetchAds(this.config.deviceId, this.state.etag);\n\n if (result.notModified) return;\n\n // Clear offline state on successful fetch\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.events.emit('online', {} as Record<string, never>);\n }\n\n this.state.ads = result.ads;\n this.state.settings = result.settings;\n this.state.etag = result.etag;\n this.state.screenId = result.screenId ?? this.state.screenId;\n this.state.programmatic = result.programmatic;\n this.state.screenOrientation = result.screenOrientation ?? this.state.screenOrientation;\n this.state.screenDimensions = result.screenDimensions ?? this.state.screenDimensions;\n\n // Update bridge context\n this.bridge.setContext(this.state.deviceId, this.state.screenId);\n\n // Update programmatic player\n this.programmaticPlayer.screenId = this.state.screenId;\n this.programmaticPlayer.vastTagUrl = this.state.programmatic?.vast_tag_url ?? null;\n this.programmaticPlayer.variantName = this.state.programmatic?.variant_name ?? null;\n if (this.state.programmatic?.sources) {\n this.programmaticPlayer.setSources(this.state.programmatic.sources);\n }\n\n // Cache ads\n if (this.state.ads.length > 0) {\n await this.cache.cacheAds(this.state.ads);\n }\n\n this.events.emit('ads_refreshed', { count: this.state.ads.length });\n } catch {\n this.state.isOffline = true;\n const cachedAds = await this.cache.getCachedAds();\n if (cachedAds.length > 0) {\n this.state.ads = cachedAds;\n this.events.emit('ads_loaded_from_cache', { count: cachedAds.length });\n }\n }\n }\n\n private startRefreshTimer(): void {\n if (this.state.refreshTimer) clearInterval(this.state.refreshTimer);\n this.state.refreshTimer = setInterval(() => {\n this.refreshAds().catch(() => {});\n }, this.config.refreshInterval);\n }\n\n private startHeartbeatTimer(): void {\n if (this.state.heartbeatTimer) clearInterval(this.state.heartbeatTimer);\n this.state.heartbeatTimer = setInterval(() => {\n this.api.sendHeartbeat(this.config.deviceId, this.state.screenId);\n }, this.config.heartbeatInterval);\n }\n\n private playNextAd(): void {\n const mode = this.state.waterfallMode;\n\n if (mode === 'programmatic_only' || mode === 'programmatic_then_direct') {\n this.playProgrammatic();\n } else if (mode === 'direct_only') {\n this.playDirect();\n }\n }\n\n private playProgrammatic(): void {\n if (this.programmaticPlayer.getIsPlaying()) return;\n\n this.state.programmaticPlaying = true;\n this.emitStateChanged();\n\n this.programmaticPlayer.play(\n () => {\n // On complete\n this.state.programmaticPlaying = false;\n this.emitStateChanged();\n this.scheduleProgrammaticRetry();\n },\n (error) => {\n // On error\n this.state.programmaticPlaying = false;\n this.state.programmaticLastError = error;\n this.state.programmaticRetryCount++;\n this.emitStateChanged();\n\n // Fall through to direct if in hybrid mode\n if (this.state.waterfallMode === 'programmatic_then_direct' && this.state.ads.length > 0) {\n this.playDirect();\n } else {\n this.scheduleProgrammaticRetry();\n }\n },\n );\n }\n\n private playDirect(): void {\n if (this.state.ads.length === 0) return;\n\n const index = this.state.currentAdIndex % this.state.ads.length;\n const ad = this.state.ads[index];\n if (!ad) return;\n\n this.directPlayer.play(\n ad,\n () => {\n this.state.currentAdIndex = (index + 1) % this.state.ads.length;\n\n // Track impression\n this.api.reportImpression({\n adid: ad.id,\n impid: ad.impression_id,\n did: this.config.deviceId,\n aid: ad.allocation_id ?? '',\n duration: ad.duration,\n completed: true,\n });\n\n this.emitStateChanged();\n this.scheduleNextDirect();\n },\n index,\n );\n }\n\n private scheduleNextDirect(): void {\n if (this.state.adTimer) clearTimeout(this.state.adTimer);\n this.state.adTimer = setTimeout(() => {\n this.playNextAd();\n }, this.config.defaultAdInterval);\n }\n\n private scheduleProgrammaticRetry(): void {\n if (this.state.programmaticRetryTimer) clearTimeout(this.state.programmaticRetryTimer);\n\n const retryCount = this.state.programmaticRetryCount;\n const baseDelay = this.config.programmaticRetry;\n const maxDelay = this.config.programmaticBackoffMax;\n const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);\n const jitter = delay * 0.2 * Math.random();\n\n this.state.programmaticRetryTimer = setTimeout(() => {\n this.prefetchNextAd().catch(() => {}).then(() => {\n this.playNextAd();\n });\n }, delay + jitter);\n }\n\n private resetProgrammaticBackoff(): void {\n this.state.programmaticRetryCount = 0;\n this.state.programmaticLastError = null;\n if (this.state.programmaticRetryTimer) {\n clearTimeout(this.state.programmaticRetryTimer);\n this.state.programmaticRetryTimer = null;\n }\n }\n\n private emitStateChanged(): void {\n this.events.emit('state_changed', this.getState());\n }\n\n private handleBridgeCommand(command: unknown): void {\n let cmd: Record<string, unknown>;\n try {\n cmd = typeof command === 'string' ? JSON.parse(command) : command as Record<string, unknown>;\n } catch {\n console.warn('[TrillboardsAds] Failed to parse bridge command:', command);\n return;\n }\n const action = (cmd as any).action || (cmd as any).command;\n const params = (cmd as any).params || (cmd as any).data || {};\n\n switch (action) {\n case 'show':\n this.show();\n break;\n case 'hide':\n this.hide();\n break;\n case 'skip':\n this.skipAd();\n break;\n case 'refresh':\n this.refresh();\n break;\n case 'getState':\n this.bridge.send('state_changed', this.getState());\n break;\n case 'configure':\n if (params.waterfall) {\n const normalized = normalizeWaterfallMode(params.waterfall);\n if (normalized) this.state.waterfallMode = normalized;\n }\n if (typeof params.volume === 'number') {\n this.state.settings.sound_enabled = params.volume > 0;\n }\n break;\n }\n }\n}\n"],"mappings":"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","names":["iife_exports","__export","TrillboardsLite","SDK_VERSION","DEFAULT_CONFIG","resolveConfig","userConfig","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","deviceId","DEFAULT_PUBLIC_STATE","createInitialState","getPublicState","internal","_a","_b","EventEmitter","event","handler","_a","data","handlers","err","wrapper","getPlayerTruth","container","width","height","rect","ApiClient","apiBase","DEFAULT_CONFIG","deviceId","etag","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","headers","controller","timeoutId","playerTruth","params","query","url","response","data","newEtag","error","impression","e","screenId","event","IndexedDBCache","maxCacheSize","resolve","reject","request","event","db","ads","tx","store","now","ad","__spreadProps","__spreadValues","toRemove","a","b","_a","_b","impression","NativeBridge","deviceId","screenId","config","_a","_b","handler","origin","_c","_d","detected","p","e","event","data","payload","CircuitBreaker","maxFailures","openDurationMs","sourceName","_a","entry","now","Telemetry","WaterfallEngine","circuitBreaker","sources","sorted","s","a","b","source","sourceName","ProgrammaticPlayer","events","timeoutMs","CircuitBreaker","Telemetry","WaterfallEngine","sources","parentElement","onComplete","onError","vastUrl","sourceName","result","correlator","finalUrl","err","resolve","settled","guardedComplete","guardedError","msg","adDisplayContainer","adsLoader","timeout","adsManagerEvent","adsManager","duration","adErrorEvent","_a","_b","_c","error","width","height","e","errorCode","errorMsg","adsRequest","_options","MAX_AD_DURATION_SECONDS","Player","events","defaultImageDuration","container","ad","onComplete","index","_a","_b","duration","durationSec","durationMs","normalizeWaterfallMode","value","normalized","_TrillboardsAds","config","resolveConfig","createInitialState","EventEmitter","ApiClient","IndexedDBCache","NativeBridge","ProgrammaticPlayer","Player","instance","e","command","bridgeEvents","eventName","data","visible","mode","prog","getPublicState","event","handler","container","_a","_b","_c","_d","_e","_f","_g","_h","result","cachedAds","error","index","ad","retryCount","baseDelay","maxDelay","delay","jitter","cmd","action","params","TrillboardsAds","TrillboardsLite","SDK_VERSION","config","_a","_b","sdkConfig","TrillboardsAds","__spreadValues","DEFAULT_PUBLIC_STATE","event","handler"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trillboards/ads-sdk",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Trillboards Ads SDK — programmatic infrastructure as a service for digital signage",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./react": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/react.d.ts",
|
|
22
|
+
"default": "./dist/react.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/react.d.cts",
|
|
26
|
+
"default": "./dist/react.cjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./react-native": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/react-native.d.ts",
|
|
32
|
+
"default": "./dist/react-native.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/react-native.d.cts",
|
|
36
|
+
"default": "./dist/react-native.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./server": {
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/server.d.ts",
|
|
42
|
+
"default": "./dist/server.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./dist/server.d.cts",
|
|
46
|
+
"default": "./dist/server.cjs"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md",
|
|
53
|
+
"CHANGELOG.md"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"dev": "tsup --watch",
|
|
58
|
+
"typecheck": "tsc --noEmit",
|
|
59
|
+
"prepublishOnly": "npm run build"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"react": ">=17.0.0",
|
|
63
|
+
"react-dom": ">=17.0.0",
|
|
64
|
+
"react-native": ">=0.70.0",
|
|
65
|
+
"react-native-webview": ">=11.0.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"react": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"react-dom": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"react-native": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react-native-webview": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@types/jest": "^30.0.0",
|
|
83
|
+
"@types/react": "^18.2.0",
|
|
84
|
+
"@types/react-native": "^0.72.0",
|
|
85
|
+
"jest": "^30.2.0",
|
|
86
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
87
|
+
"jest-util": "^30.2.0",
|
|
88
|
+
"ts-jest": "^29.4.6",
|
|
89
|
+
"tsup": "^8.0.0",
|
|
90
|
+
"typescript": "^5.3.0"
|
|
91
|
+
},
|
|
92
|
+
"keywords": [
|
|
93
|
+
"trillboards",
|
|
94
|
+
"ads",
|
|
95
|
+
"sdk",
|
|
96
|
+
"digital-signage",
|
|
97
|
+
"dooh",
|
|
98
|
+
"ctv",
|
|
99
|
+
"programmatic",
|
|
100
|
+
"vast",
|
|
101
|
+
"openrtb"
|
|
102
|
+
],
|
|
103
|
+
"license": "MIT",
|
|
104
|
+
"repository": {
|
|
105
|
+
"type": "git",
|
|
106
|
+
"url": "https://github.com/trillboards/packages.git",
|
|
107
|
+
"directory": "trillboards-ads-sdk"
|
|
108
|
+
}
|
|
109
|
+
}
|