@action-x/ad-sdk 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -70,7 +70,7 @@
70
70
  </div>
71
71
  </div>
72
72
  `}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
73
- `)}}const N="ad_session_id";function D(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function H(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function _(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class U{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||N,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const F=new U,m=()=>F.getSessionId();class k{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let s=null;for(let n=0;n<=this.retryAttempts;n++){const r=this.debug||H();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${n+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),d={"Content-Type":"application/json"},l=D();l&&(d["X-API-Key"]=l,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const u=await fetch(e,{method:"POST",headers:d,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!u.ok)throw new Error(`HTTP ${u.status}: ${u.statusText}`);const h=await u.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}catch(a){s=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${n+1}):`,a),n<this.retryAttempts&&await this.delay(this.retryDelay*(n+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new k,x=(o,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackImpression(o):O.trackImpression(o),I=(o,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackClick(o):O.trackClick(o);function G(o,e){return`vt_${o}_${e}`}async function j(o){return Promise.all(o.map(e=>x(e)))}async function z(o){return Promise.all(o.map(e=>I(e)))}function W(o){const e=new k(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function Q(o={}){const e=new U(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const M=class M{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=w.renderActionCard(e,i);const s=t.querySelector(".ax-ad-card-link");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const s=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const n=t.querySelector(".ax-ad-suffix-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const s=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const n=t.querySelector(".ax-ad-source-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),s){t.innerHTML="";const n=document.createElement("div");return n.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,s),n.appendChild(a)}),t.appendChild(n),t}static handleClick(e,t){const{analytics:i,onClick:s}=t,n=this.getClickUrl(e);if(!n){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}s&&s(e),i&&I({requestId:i.requestId,adId:e.original.id,destinationUrl:n,slotId:i.slotId,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,userId:i.userId},{baseUrl:i.apiBaseUrl}).catch(r=>{console.error("[DOMRenderer] Analytics click tracking failed:",r)}),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",n),window.open(n,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:s,onImpression:n}=i,r=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),d())},d=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{n&&n(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):n&&n(e)};if(typeof IntersectionObserver<"u"){let l=!1,u=null;const h=new IntersectionObserver(f=>{f.forEach(v=>{l=v.isIntersecting&&v.intersectionRatio>=.5,l?u||(u=setTimeout(()=>{u=null,l&&(c(),h.disconnect())},1e3)):u&&(clearTimeout(u),u=null)})},{threshold:.5}),p=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;h.observe(p)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",s=e.adapted.body||"";return t==="inline"?`
73
+ `)}}const N="ad_session_id";function D(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function H(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function _(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class ${constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||N,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const F=new $,b=()=>F.getSessionId();class v{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let s=null;for(let n=0;n<=this.retryAttempts;n++){const r=this.debug||H();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${n+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),d={"Content-Type":"application/json"},l=D();l&&(d["X-API-Key"]=l,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const u=await fetch(e,{method:"POST",headers:d,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!u.ok)throw new Error(`HTTP ${u.status}: ${u.statusText}`);const h=await u.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}catch(a){s=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${n+1}):`,a),n<this.retryAttempts&&await this.delay(this.retryDelay*(n+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new v,x=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackImpression(o):O.trackImpression(o),U=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackClick(o):O.trackClick(o);function G(o,e){return`vt_${o}_${e}`}async function j(o){return Promise.all(o.map(e=>x(e)))}async function z(o){return Promise.all(o.map(e=>U(e)))}function W(o){const e=new v(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function Q(o={}){const e=new $(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const M=class M{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=w.renderActionCard(e,i);const s=t.querySelector(".ax-ad-card-link");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const s=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const n=t.querySelector(".ax-ad-suffix-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const s=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const n=t.querySelector(".ax-ad-source-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),s){t.innerHTML="";const n=document.createElement("div");return n.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,s),n.appendChild(a)}),t.appendChild(n),t}static handleClick(e,t){const{onClick:i}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}i&&i(e),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:s,onImpression:n}=i,r=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),d())},d=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:b(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{n&&n(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):n&&n(e)};if(typeof IntersectionObserver<"u"){let l=!1,u=null;const h=new IntersectionObserver(f=>{f.forEach(k=>{l=k.isIntersecting&&k.intersectionRatio>=.5,l?u||(u=setTimeout(()=>{u=null,l&&(c(),h.disconnect())},1e3)):u&&(clearTimeout(u),u=null)})},{threshold:.5}),p=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;h.observe(p)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",s=e.adapted.body||"";return t==="inline"?`
74
74
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
75
75
  ${i}
76
76
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
@@ -127,4 +127,4 @@
127
127
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
128
128
  </svg>
129
129
  </div>
130
- `}};M.trackedImpressionKeys=new Set;let y=M;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,n;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const i=(n=document.querySelector('meta[name="application-name"]'))==null?void 0:n.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const s=i.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var i,s;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const n=e.match(/Mac OS X (\d+[._]\d+)/);return n?n[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const n=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(n)return n[1]+" "+n[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(o){return T().collect(o)}function J(){return b().device}function B(){return b().user}function X(){return b().app}function Z(){return b().geo}function q(){return B().id}function Y(){T().clearCache()}function ee(o){const e=b(o);return JSON.stringify(e,null,2)}function te(){var t;const o=b();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function V(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ie(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",s=T().collect(),n={...o,clientInfo:s},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),V()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(V()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function E(o={}){const e={variant:o.variant??C.variant,count:o.count??C.count,preferences:{...C.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new K,this.slots_config=E(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),s=g.inFlightRequests.get(i);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const n=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??m()}:{userId:this.currentUserId,sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:r.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,n),n}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return q()}catch{return}}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,r=s?e:t,a=(s?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[n];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",n),null;const d=a.adIndex??0,l=c.ads[d];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,n),null;const u=this.adsAnalyticsMap.get(l.original.id),h=this.slots_config.find(v=>v.slotId===n),p=(h==null?void 0:h.format)||"action_card";r.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?y.renderSuffixAd(l,r,f):p==="source"?y.renderSponsoredSource(l,r,f):p==="lead_gen"?y.renderLeadGenAd(l,r,f):y.renderActionCard(l,r,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=E(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const s=this.adsAnalyticsMap.get(i);s&&t.set(i,s)}),t}async trackAdClick(e,t,i){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),r=await I({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:n,slotId:s.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),n=await x({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return n&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",n.eventId)),n}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let $=g;function P(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class R{constructor(e={},t="/api/v1"){var i,s;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((s=e.batchConfig)==null?void 0:s.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,s,n){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,s,n),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,s,n);const c=setTimeout(()=>{this.endTracking(e,i,s,n)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,s,n){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!r.enteredViewportAt&&(r.enteredViewportAt=d,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&r.enteredViewportAt){const u=d-r.enteredViewportAt;r.totalVisibleTimeMs+=u,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:d})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,s){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const l=c-a.enteredViewportAt;a.currentVisibleTimeMs=l,l>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var n,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const s=((n=this.config.batchConfig)==null?void 0:n.maxBatchSize)??5;if(i.length>=s)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const s={"Content-Type":"application/json"},n=P();n&&(s["x-api-key"]=n),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:s,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(s){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,s),t.unshift(...i)}}endTracking(e,t,i,s){const n=this.metrics.get(e);if(!n)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"end_tracking",visiblePercentage:n.visiblePercentage,maxVisiblePercentage:n.maxVisiblePercentage,totalVisibleTimeMs:n.totalVisibleTimeMs,isViewable:n.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const s=this.timers.get(`${e}_monitoring`);s&&(clearInterval(s),this.timers.delete(`${e}_monitoring`));const n=this.batchTimers.get(e);n&&(clearTimeout(n),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},s=P();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let A=null;function ne(o,e){return A||(A=new R(o,e),A.setupBeforeUnload()),A}const re="0.1.0";exports.AdManager=$;exports.AnalyticsSender=k;exports.ClientInfoCollector=S;exports.DOMRenderer=y;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=U;exports.ViewabilityTracker=R;exports.adaptAdToKoahAd=ie;exports.clearClientInfoCache=Y;exports.createAnalytics=W;exports.createSession=Q;exports.fetchAds=L;exports.generateViewToken=G;exports.getAppInfo=X;exports.getClientInfo=b;exports.getClientInfoCollector=T;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=J;exports.getGeoInfo=Z;exports.getSessionId=m;exports.getUserId=q;exports.getUserInfo=B;exports.getViewabilityTracker=ne;exports.trackAdClick=I;exports.trackAdImpression=x;exports.trackClicksBatch=z;exports.trackImpressionsBatch=j;
130
+ `}};M.trackedImpressionKeys=new Set;let m=M;class I{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,n;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const i=(n=document.querySelector('meta[name="application-name"]'))==null?void 0:n.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const s=i.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var i,s;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new I),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const n=e.match(/Mac OS X (\d+[._]\d+)/);return n?n[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const n=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(n)return n[1]+" "+n[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const S=()=>I.getInstance();function y(o){return S().collect(o)}function J(){return y().device}function B(){return y().user}function X(){return y().app}function Z(){return y().geo}function q(){return B().id}function Y(){S().clearCache()}function ee(o){const e=y(o);return JSON.stringify(e,null,2)}function te(){var t;const o=y();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function V(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ie(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",s=S().collect(),n={...o,clientInfo:s},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),V()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(V()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},T={variant:"horizontal",count:1,preferences:{}};function E(o={}){const e={variant:o.variant??T.variant,count:o.count??T.count,preferences:{...T.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new K,this.slots_config=E(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),s=g.inFlightRequests.get(i);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const n=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??b()}:{userId:this.currentUserId,sessionId:b()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:r.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,n),n}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return q()}catch{return}}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,r=s?e:t,a=(s?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[n];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",n),null;const d=a.adIndex??0,l=c.ads[d];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,n),null;const u=this.adsAnalyticsMap.get(l.original.id),h=this.slots_config.find(k=>k.slotId===n),p=(h==null?void 0:h.format)||"action_card";r.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?m.renderSuffixAd(l,r,f):p==="source"?m.renderSponsoredSource(l,r,f):p==="lead_gen"?m.renderLeadGenAd(l,r,f):m.renderActionCard(l,r,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=E(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const s=this.adsAnalyticsMap.get(i);s&&t.set(i,s)}),t}async trackAdClick(e,t,i){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=b(),r=await U({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:n,slotId:s.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=b(),n=await x({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return n&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",n.eventId)),n}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let C=g;function P(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class R{constructor(e={},t="/api/v1"){var i,s;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((s=e.batchConfig)==null?void 0:s.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,s,n){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,s,n),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,s,n);const c=setTimeout(()=>{this.endTracking(e,i,s,n)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,s,n){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!r.enteredViewportAt&&(r.enteredViewportAt=d,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&r.enteredViewportAt){const u=d-r.enteredViewportAt;r.totalVisibleTimeMs+=u,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:d})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,s){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const l=c-a.enteredViewportAt;a.currentVisibleTimeMs=l,l>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var n,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const s=((n=this.config.batchConfig)==null?void 0:n.maxBatchSize)??5;if(i.length>=s)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const s={"Content-Type":"application/json"},n=P();n&&(s["x-api-key"]=n),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:s,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(s){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,s),t.unshift(...i)}}endTracking(e,t,i,s){const n=this.metrics.get(e);if(!n)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"end_tracking",visiblePercentage:n.visiblePercentage,maxVisiblePercentage:n.maxVisiblePercentage,totalVisibleTimeMs:n.totalVisibleTimeMs,isViewable:n.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const s=this.timers.get(`${e}_monitoring`);s&&(clearInterval(s),this.timers.delete(`${e}_monitoring`));const n=this.batchTimers.get(e);n&&(clearTimeout(n),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},s=P();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let A=null;function ne(o,e){return A||(A=new R(o,e),A.setupBeforeUnload()),A}const re="0.1.0";exports.AdManager=C;exports.AnalyticsSender=v;exports.ClientInfoCollector=I;exports.DOMRenderer=m;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=$;exports.ViewabilityTracker=R;exports.adaptAdToKoahAd=ie;exports.clearClientInfoCache=Y;exports.createAnalytics=W;exports.createSession=Q;exports.fetchAds=L;exports.generateViewToken=G;exports.getAppInfo=X;exports.getClientInfo=y;exports.getClientInfoCollector=S;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=J;exports.getGeoInfo=Z;exports.getSessionId=b;exports.getUserId=q;exports.getUserInfo=B;exports.getViewabilityTracker=ne;exports.trackAdClick=U;exports.trackAdImpression=x;exports.trackClicksBatch=z;exports.trackImpressionsBatch=j;
package/dist/index.d.ts CHANGED
@@ -748,7 +748,7 @@ export declare class DOMRenderer {
748
748
  static renderAds(ads: AdaptedAdContent[], container: HTMLElement, renderFn?: (ad: AdaptedAdContent, container: HTMLElement, options?: any) => void, options?: any): HTMLElement;
749
749
  /**
750
750
  * Handle ad click
751
- * First tracks the click (async, non-blocking), then opens the URL.
751
+ * Directly opens click_url in a new window.
752
752
  */
753
753
  private static handleClick;
754
754
  /**
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ class w {
66
66
  alt="${n.title}"
67
67
  class="ax-ad-image"
68
68
  loading="lazy"
69
- />` : "", d = (n.brand || "").trim(), l = d && d.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${n.brand}</span>` : "", u = `
69
+ />` : "", u = (n.brand || "").trim(), l = u && u.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${n.brand}</span>` : "", d = `
70
70
  ${c}
71
71
  <div class="ax-ad-content">
72
72
  ${l}
@@ -81,7 +81,7 @@ class w {
81
81
  data-ad-id="${e.original.id}"
82
82
  data-impression-url="${a}"
83
83
  >` : "", p = s ? "</a>" : "";
84
- return h + u + p;
84
+ return h + d + p;
85
85
  }
86
86
  /**
87
87
  * Render Suffix ad as HTML string
@@ -190,7 +190,7 @@ function R() {
190
190
  var o;
191
191
  return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
192
192
  }
193
- function U(o) {
193
+ function C(o) {
194
194
  if (o) return o;
195
195
  if (typeof window < "u") {
196
196
  const e = window.__AD_CONFIG__;
@@ -199,7 +199,7 @@ function U(o) {
199
199
  }
200
200
  return "/api/v1";
201
201
  }
202
- class P {
202
+ class E {
203
203
  constructor(e = {}) {
204
204
  this.memoryCache = /* @__PURE__ */ new Map(), this.sessionKey = e.sessionKey || q, this.storage = e.storage || "sessionStorage", (typeof window > "u" || typeof window.sessionStorage > "u") && (this.storage = "memory");
205
205
  }
@@ -235,7 +235,7 @@ class P {
235
235
  this.storage === "sessionStorage" ? sessionStorage.removeItem(this.sessionKey) : this.memoryCache.delete(this.sessionKey);
236
236
  }
237
237
  }
238
- const K = new P(), m = () => K.getSessionId();
238
+ const K = new E(), b = () => K.getSessionId();
239
239
  class x {
240
240
  constructor(e = {}) {
241
241
  this.explicitBaseUrl = e.baseUrl, this.timeout = e.timeout || 1e4, this.retryAttempts = e.retryAttempts ?? 2, this.retryDelay = e.retryDelay || 1e3, this.debug = e.debug || !1;
@@ -245,7 +245,7 @@ class x {
245
245
  */
246
246
  async trackImpression(e) {
247
247
  return this.sendRequest(
248
- `${U(this.explicitBaseUrl)}/ads/impression`,
248
+ `${C(this.explicitBaseUrl)}/ads/impression`,
249
249
  e,
250
250
  "impression"
251
251
  );
@@ -255,7 +255,7 @@ class x {
255
255
  */
256
256
  async trackClick(e) {
257
257
  return this.sendRequest(
258
- `${U(this.explicitBaseUrl)}/ads/click`,
258
+ `${C(this.explicitBaseUrl)}/ads/click`,
259
259
  e,
260
260
  "click"
261
261
  );
@@ -270,20 +270,20 @@ class x {
270
270
  const r = this.debug || R();
271
271
  try {
272
272
  r && (console.log(`[Analytics] Request URL (${i}):`, e), console.log(`[Analytics] Sending ${i} event (attempt ${n + 1}):`, t));
273
- const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), d = {
273
+ const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), u = {
274
274
  "Content-Type": "application/json"
275
275
  }, l = L();
276
- l && (d["X-API-Key"] = l, r && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
277
- const u = await fetch(e, {
276
+ l && (u["X-API-Key"] = l, r && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
277
+ const d = await fetch(e, {
278
278
  method: "POST",
279
- headers: d,
279
+ headers: u,
280
280
  body: JSON.stringify(t),
281
281
  keepalive: !0,
282
282
  signal: a.signal
283
283
  });
284
- if (clearTimeout(c), !u.ok)
285
- throw new Error(`HTTP ${u.status}: ${u.statusText}`);
286
- const h = await u.json();
284
+ if (clearTimeout(c), !d.ok)
285
+ throw new Error(`HTTP ${d.status}: ${d.statusText}`);
286
+ const h = await d.json();
287
287
  return r && console.log(`[Analytics] ${i} event tracked successfully:`, h), h;
288
288
  } catch (a) {
289
289
  s = a, r && console.warn(`[Analytics] ${i} tracking failed (attempt ${n + 1}):`, a), n < this.retryAttempts && await this.delay(this.retryDelay * (n + 1));
@@ -298,7 +298,7 @@ class x {
298
298
  return new Promise((t) => setTimeout(t, e));
299
299
  }
300
300
  }
301
- const O = new x(), I = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackImpression(o) : O.trackImpression(o), S = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackClick(o) : O.trackClick(o);
301
+ const P = new x(), I = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackImpression(o) : P.trackImpression(o), O = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackClick(o) : P.trackClick(o);
302
302
  function z(o, e) {
303
303
  return `vt_${o}_${e}`;
304
304
  }
@@ -306,7 +306,7 @@ async function j(o) {
306
306
  return Promise.all(o.map((e) => I(e)));
307
307
  }
308
308
  async function W(o) {
309
- return Promise.all(o.map((e) => S(e)));
309
+ return Promise.all(o.map((e) => O(e)));
310
310
  }
311
311
  function Q(o) {
312
312
  const e = new x(o);
@@ -316,14 +316,14 @@ function Q(o) {
316
316
  };
317
317
  }
318
318
  function J(o = {}) {
319
- const e = new P(o);
319
+ const e = new E(o);
320
320
  return {
321
321
  getSessionId: () => e.getSessionId(),
322
322
  regenerateSessionId: () => e.regenerateSessionId(),
323
323
  clearSessionId: () => e.clearSessionId()
324
324
  };
325
325
  }
326
- const C = class C {
326
+ const $ = class $ {
327
327
  static isDebugEnabled() {
328
328
  var e;
329
329
  return typeof window < "u" ? !!((e = window.__AD_CONFIG__) != null && e.debug) : !1;
@@ -389,26 +389,15 @@ const C = class C {
389
389
  }
390
390
  /**
391
391
  * Handle ad click
392
- * First tracks the click (async, non-blocking), then opens the URL.
392
+ * Directly opens click_url in a new window.
393
393
  */
394
394
  static handleClick(e, t) {
395
- const { analytics: i, onClick: s } = t, n = this.getClickUrl(e);
396
- if (!n) {
395
+ const { onClick: i } = t, s = this.getClickUrl(e);
396
+ if (!s) {
397
397
  console.warn("[DOMRenderer] Missing click url for ad:", e.original.id);
398
398
  return;
399
399
  }
400
- s && s(e), i && S({
401
- requestId: i.requestId,
402
- adId: e.original.id,
403
- destinationUrl: n,
404
- slotId: i.slotId,
405
- sessionId: m(),
406
- adTitle: e.adapted.title,
407
- format: e.original.type,
408
- userId: i.userId
409
- }, { baseUrl: i.apiBaseUrl }).catch((r) => {
410
- console.error("[DOMRenderer] Analytics click tracking failed:", r);
411
- }), this.isDebugEnabled() && console.log("[DOMRenderer] Redirect URL:", n), window.open(n, "_blank", "noopener,noreferrer");
400
+ i && i(e), this.isDebugEnabled() && console.log("[DOMRenderer] Redirect URL:", s), window.open(s, "_blank", "noopener,noreferrer");
412
401
  }
413
402
  /**
414
403
  * Track ad impression using IntersectionObserver (50% visible + 1000ms delay)
@@ -420,15 +409,15 @@ const C = class C {
420
409
  return;
421
410
  let a = !1;
422
411
  const c = () => {
423
- a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), d());
424
- }, d = () => {
412
+ a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), u());
413
+ }, u = () => {
425
414
  s ? I({
426
415
  requestId: s.requestId,
427
416
  adId: e.original.id,
428
417
  slotId: s.slotId,
429
418
  position: s.position,
430
419
  totalAds: s.totalAds,
431
- sessionId: m(),
420
+ sessionId: b(),
432
421
  adTitle: e.adapted.title,
433
422
  format: e.original.type,
434
423
  source: "internal",
@@ -441,13 +430,13 @@ const C = class C {
441
430
  }) : n && n(e);
442
431
  };
443
432
  if (typeof IntersectionObserver < "u") {
444
- let l = !1, u = null;
433
+ let l = !1, d = null;
445
434
  const h = new IntersectionObserver(
446
435
  (f) => {
447
436
  f.forEach((v) => {
448
- l = v.isIntersecting && v.intersectionRatio >= 0.5, l ? u || (u = setTimeout(() => {
449
- u = null, l && (c(), h.disconnect());
450
- }, 1e3)) : u && (clearTimeout(u), u = null);
437
+ l = v.isIntersecting && v.intersectionRatio >= 0.5, l ? d || (d = setTimeout(() => {
438
+ d = null, l && (c(), h.disconnect());
439
+ }, 1e3)) : d && (clearTimeout(d), d = null);
451
440
  });
452
441
  },
453
442
  { threshold: 0.5 }
@@ -529,9 +518,9 @@ const C = class C {
529
518
  `;
530
519
  }
531
520
  };
532
- C.trackedImpressionKeys = /* @__PURE__ */ new Set();
533
- let y = C;
534
- class T {
521
+ $.trackedImpressionKeys = /* @__PURE__ */ new Set();
522
+ let m = $;
523
+ class S {
535
524
  constructor() {
536
525
  this.cachedStaticInfo = null, this.cacheTimestamp = 0, this.CACHE_TTL = 36e5;
537
526
  }
@@ -618,7 +607,7 @@ class T {
618
607
  * Get singleton instance
619
608
  */
620
609
  static getInstance() {
621
- return this.instance || (this.instance = new T()), this.instance;
610
+ return this.instance || (this.instance = new S()), this.instance;
622
611
  }
623
612
  /**
624
613
  * Collect all available client information
@@ -921,35 +910,35 @@ class T {
921
910
  this.cachedStaticInfo = null, this.cacheTimestamp = 0;
922
911
  }
923
912
  }
924
- const $ = () => T.getInstance();
925
- function b(o) {
926
- return $().collect(o);
913
+ const T = () => S.getInstance();
914
+ function y(o) {
915
+ return T().collect(o);
927
916
  }
928
917
  function X() {
929
- return b().device;
918
+ return y().device;
930
919
  }
931
920
  function N() {
932
- return b().user;
921
+ return y().user;
933
922
  }
934
923
  function Z() {
935
- return b().app;
924
+ return y().app;
936
925
  }
937
926
  function Y() {
938
- return b().geo;
927
+ return y().geo;
939
928
  }
940
- function D() {
929
+ function H() {
941
930
  return N().id;
942
931
  }
943
932
  function ee() {
944
- $().clearCache();
933
+ T().clearCache();
945
934
  }
946
935
  function te(o) {
947
- const e = b(o);
936
+ const e = y(o);
948
937
  return JSON.stringify(e, null, 2);
949
938
  }
950
939
  function ie() {
951
940
  var t;
952
- const o = b();
941
+ const o = y();
953
942
  return [
954
943
  o.device.os,
955
944
  o.device.osv,
@@ -958,7 +947,7 @@ function ie() {
958
947
  ((t = o.geo) == null ? void 0 : t.country) || "Unknown"
959
948
  ].join(" / ");
960
949
  }
961
- function M() {
950
+ function U() {
962
951
  var o;
963
952
  return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
964
953
  }
@@ -989,15 +978,15 @@ function se(o) {
989
978
  }
990
979
  };
991
980
  }
992
- async function H(o, e = {}) {
993
- const t = e.apiBaseUrl || "/api/v1", s = $().collect(), n = {
981
+ async function D(o, e = {}) {
982
+ const t = e.apiBaseUrl || "/api/v1", s = T().collect(), n = {
994
983
  ...o,
995
984
  clientInfo: s
996
985
  // Auto-injected
997
986
  }, r = {
998
987
  "Content-Type": "application/json"
999
988
  };
1000
- e.apiKey && (r["X-API-Key"] = e.apiKey), M() && (console.log("[fetchAds] Request URL:", `${t}/ads/request`), console.log("[fetchAds] Request params:", o), console.log("[fetchAds] Request body:", n));
989
+ e.apiKey && (r["X-API-Key"] = e.apiKey), U() && (console.log("[fetchAds] Request URL:", `${t}/ads/request`), console.log("[fetchAds] Request params:", o), console.log("[fetchAds] Request body:", n));
1001
990
  const a = await fetch(`${t}/ads/request`, {
1002
991
  method: "POST",
1003
992
  headers: r,
@@ -1006,7 +995,7 @@ async function H(o, e = {}) {
1006
995
  if (!a.ok)
1007
996
  throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);
1008
997
  const c = await a.json();
1009
- if (M() && console.log("[fetchAds] Response:", c), !c.success)
998
+ if (U() && console.log("[fetchAds] Response:", c), !c.success)
1010
999
  throw new Error(c.error || "Ad request failed");
1011
1000
  return c.data;
1012
1001
  }
@@ -1015,7 +1004,7 @@ const F = { width: 400, height: 200 }, A = {
1015
1004
  count: 1,
1016
1005
  preferences: {}
1017
1006
  };
1018
- function _(o = {}) {
1007
+ function M(o = {}) {
1019
1008
  const e = {
1020
1009
  variant: o.variant ?? A.variant,
1021
1010
  count: o.count ?? A.count,
@@ -1036,7 +1025,7 @@ function _(o = {}) {
1036
1025
  }
1037
1026
  const g = class g {
1038
1027
  constructor(e) {
1039
- this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new B(), this.slots_config = _(e.cardOption), typeof window < "u" && (window.__AD_CONFIG__ = {
1028
+ this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new B(), this.slots_config = M(e.cardOption), typeof window < "u" && (window.__AD_CONFIG__ = {
1040
1029
  ...window.__AD_CONFIG__ || {},
1041
1030
  apiKey: e.apiKey,
1042
1031
  apiBaseUrl: e.apiBaseUrl,
@@ -1060,14 +1049,14 @@ const g = class g {
1060
1049
  this.isLoading = !0, this.eventBus.emit("adsLoading"), this.config.debug && console.log("[AdManager] Starting ad request...");
1061
1050
  const n = (async () => {
1062
1051
  try {
1063
- const r = await H(
1052
+ const r = await D(
1064
1053
  {
1065
1054
  conversationContext: t.conversationContext,
1066
1055
  userContext: t.userContext ? {
1067
1056
  ...t.userContext,
1068
1057
  userId: t.userContext.userId ?? this.currentUserId,
1069
- sessionId: t.userContext.sessionId ?? m()
1070
- } : { userId: this.currentUserId, sessionId: m() },
1058
+ sessionId: t.userContext.sessionId ?? b()
1059
+ } : { userId: this.currentUserId, sessionId: b() },
1071
1060
  slots: this.slots_config
1072
1061
  },
1073
1062
  {
@@ -1076,9 +1065,9 @@ const g = class g {
1076
1065
  }
1077
1066
  );
1078
1067
  this.currentRequestId = r.requestId, this.adsAnalyticsMap.clear(), r.slots.forEach((c) => {
1079
- c.ads.forEach((d, l) => {
1080
- const u = d.original.id;
1081
- this.adsAnalyticsMap.set(u, {
1068
+ c.ads.forEach((u, l) => {
1069
+ const d = u.original.id;
1070
+ this.adsAnalyticsMap.set(d, {
1082
1071
  requestId: r.requestId,
1083
1072
  slotId: c.slotId,
1084
1073
  position: l,
@@ -1117,7 +1106,7 @@ const g = class g {
1117
1106
  if (e != null && e.userId)
1118
1107
  return e.userId;
1119
1108
  try {
1120
- return D();
1109
+ return H();
1121
1110
  } catch {
1122
1111
  return;
1123
1112
  }
@@ -1129,18 +1118,18 @@ const g = class g {
1129
1118
  const c = this.slots[n];
1130
1119
  if (!c || !c.ads || c.ads.length === 0)
1131
1120
  return this.config.debug && console.warn("[AdManager] No ads in slot:", n), null;
1132
- const d = a.adIndex ?? 0, l = c.ads[d];
1121
+ const u = a.adIndex ?? 0, l = c.ads[u];
1133
1122
  if (!l)
1134
- return this.config.debug && console.warn(`[AdManager] Ad at index ${d} not found in slot:`, n), null;
1135
- const u = this.adsAnalyticsMap.get(l.original.id), h = this.slots_config.find((v) => v.slotId === n), p = (h == null ? void 0 : h.format) || "action_card";
1123
+ return this.config.debug && console.warn(`[AdManager] Ad at index ${u} not found in slot:`, n), null;
1124
+ const d = this.adsAnalyticsMap.get(l.original.id), h = this.slots_config.find((v) => v.slotId === n), p = (h == null ? void 0 : h.format) || "action_card";
1136
1125
  r.innerHTML = "";
1137
1126
  const f = {
1138
- analytics: u,
1127
+ analytics: d,
1139
1128
  variant: a.variant,
1140
1129
  onClick: a.onClick,
1141
1130
  onImpression: a.onImpression
1142
1131
  };
1143
- return p === "suffix" ? y.renderSuffixAd(l, r, f) : p === "source" ? y.renderSponsoredSource(l, r, f) : p === "lead_gen" ? y.renderLeadGenAd(l, r, f) : y.renderActionCard(l, r, f);
1132
+ return p === "suffix" ? m.renderSuffixAd(l, r, f) : p === "source" ? m.renderSponsoredSource(l, r, f) : p === "lead_gen" ? m.renderLeadGenAd(l, r, f) : m.renderActionCard(l, r, f);
1144
1133
  }
1145
1134
  /**
1146
1135
  * Get ad slots data
@@ -1179,7 +1168,7 @@ const g = class g {
1179
1168
  * Update configuration
1180
1169
  */
1181
1170
  updateConfig(e) {
1182
- this.config = { ...this.config, ...e }, e.cardOption !== void 0 && (this.slots_config = _(this.config.cardOption)), this.config.debug && console.log("[AdManager] Config updated:", e);
1171
+ this.config = { ...this.config, ...e }, e.cardOption !== void 0 && (this.slots_config = M(this.config.cardOption)), this.config.debug && console.log("[AdManager] Config updated:", e);
1183
1172
  }
1184
1173
  /**
1185
1174
  * Clear all slot data
@@ -1234,7 +1223,7 @@ const g = class g {
1234
1223
  const s = this.adsAnalyticsMap.get(e);
1235
1224
  if (!s || !this.currentRequestId)
1236
1225
  return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
1237
- const n = m(), r = await S({
1226
+ const n = b(), r = await O({
1238
1227
  requestId: this.currentRequestId,
1239
1228
  adId: e,
1240
1229
  destinationUrl: t,
@@ -1254,7 +1243,7 @@ const g = class g {
1254
1243
  const i = this.adsAnalyticsMap.get(e);
1255
1244
  if (!i || !this.currentRequestId)
1256
1245
  return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
1257
- const s = m(), n = await I({
1246
+ const s = b(), n = await I({
1258
1247
  requestId: this.currentRequestId,
1259
1248
  adId: e,
1260
1249
  slotId: i.slotId,
@@ -1277,8 +1266,8 @@ const g = class g {
1277
1266
  }
1278
1267
  };
1279
1268
  g.DEFAULT_SLOT_ID = "action_card", g.inFlightRequests = /* @__PURE__ */ new Map();
1280
- let V = g;
1281
- function E() {
1269
+ let _ = g;
1270
+ function V() {
1282
1271
  if (typeof window < "u") {
1283
1272
  const o = window.__AD_CONFIG__;
1284
1273
  if (o != null && o.apiKey)
@@ -1320,7 +1309,7 @@ class G {
1320
1309
  };
1321
1310
  this.metrics.set(e, r), this.isTracking.set(e, !0), this.eventQueue.set(e, []);
1322
1311
  const a = new IntersectionObserver(
1323
- (d) => this.handleIntersection(e, d[0], i, s, n),
1312
+ (u) => this.handleIntersection(e, u[0], i, s, n),
1324
1313
  {
1325
1314
  threshold: this.createThresholds()
1326
1315
  }
@@ -1352,8 +1341,8 @@ class G {
1352
1341
  if (!r || !this.isTracking.get(e)) return;
1353
1342
  const a = r.isViewable, c = Math.round(t.intersectionRatio * 100);
1354
1343
  r.visiblePercentage = c, r.maxVisiblePercentage = Math.max(r.maxVisiblePercentage, c);
1355
- const d = Date.now(), l = c >= this.config.minVisiblePercentage;
1356
- if (l && !r.enteredViewportAt && (r.enteredViewportAt = d, r.enterCount++, this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`), this.queueEvent(e, {
1344
+ const u = Date.now(), l = c >= this.config.minVisiblePercentage;
1345
+ if (l && !r.enteredViewportAt && (r.enteredViewportAt = u, r.enterCount++, this.log(`[ViewabilityTracker] ${e} entered viewport at ${u}`), this.queueEvent(e, {
1357
1346
  adId: e,
1358
1347
  sessionId: i,
1359
1348
  requestId: s,
@@ -1363,10 +1352,10 @@ class G {
1363
1352
  maxVisiblePercentage: r.maxVisiblePercentage,
1364
1353
  totalVisibleTimeMs: r.totalVisibleTimeMs,
1365
1354
  isViewable: !1,
1366
- timestamp: d
1355
+ timestamp: u
1367
1356
  })), !l && r.enteredViewportAt) {
1368
- const u = d - r.enteredViewportAt;
1369
- r.totalVisibleTimeMs += u, r.enteredViewportAt = null, r.currentVisibleTimeMs = 0, this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`), this.queueEvent(e, {
1357
+ const d = u - r.enteredViewportAt;
1358
+ r.totalVisibleTimeMs += d, r.enteredViewportAt = null, r.currentVisibleTimeMs = 0, this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`), this.queueEvent(e, {
1370
1359
  adId: e,
1371
1360
  sessionId: i,
1372
1361
  requestId: s,
@@ -1376,7 +1365,7 @@ class G {
1376
1365
  maxVisiblePercentage: r.maxVisiblePercentage,
1377
1366
  totalVisibleTimeMs: r.totalVisibleTimeMs,
1378
1367
  isViewable: r.isViewable,
1379
- timestamp: d
1368
+ timestamp: u
1380
1369
  });
1381
1370
  }
1382
1371
  r.isViewable !== a && r.isViewable && this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`), this.metrics.set(e, r);
@@ -1392,10 +1381,10 @@ class G {
1392
1381
  clearInterval(r);
1393
1382
  return;
1394
1383
  }
1395
- const c = Date.now(), d = a.isViewable;
1384
+ const c = Date.now(), u = a.isViewable;
1396
1385
  if (a.enteredViewportAt) {
1397
1386
  const l = c - a.enteredViewportAt;
1398
- a.currentVisibleTimeMs = l, l >= this.config.minViewableDuration && a.visiblePercentage >= this.config.minVisiblePercentage && (a.isViewable = !0, d || (a.viewableAt = a.enteredViewportAt, this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`), this.queueEvent(e, {
1387
+ a.currentVisibleTimeMs = l, l >= this.config.minViewableDuration && a.visiblePercentage >= this.config.minVisiblePercentage && (a.isViewable = !0, u || (a.viewableAt = a.enteredViewportAt, this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`), this.queueEvent(e, {
1399
1388
  adId: e,
1400
1389
  sessionId: t,
1401
1390
  requestId: i,
@@ -1429,10 +1418,10 @@ class G {
1429
1418
  else {
1430
1419
  const a = this.batchTimers.get(e);
1431
1420
  a && clearTimeout(a);
1432
- const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4, d = setTimeout(() => {
1421
+ const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4, u = setTimeout(() => {
1433
1422
  this.flushQueue(e);
1434
1423
  }, c);
1435
- this.batchTimers.set(e, d);
1424
+ this.batchTimers.set(e, u);
1436
1425
  }
1437
1426
  }
1438
1427
  /**
@@ -1446,7 +1435,7 @@ class G {
1446
1435
  try {
1447
1436
  const s = {
1448
1437
  "Content-Type": "application/json"
1449
- }, n = E();
1438
+ }, n = V();
1450
1439
  n && (s["x-api-key"] = n), await fetch(`${this.baseUrl}/ads/viewability/batch`, {
1451
1440
  method: "POST",
1452
1441
  headers: s,
@@ -1520,7 +1509,7 @@ class G {
1520
1509
  if (t && t.length > 0) {
1521
1510
  const i = {
1522
1511
  "Content-Type": "application/json"
1523
- }, s = E();
1512
+ }, s = V();
1524
1513
  s && (i["x-api-key"] = s), fetch(`${this.baseUrl}/ads/viewability/batch`, {
1525
1514
  method: "POST",
1526
1515
  headers: i,
@@ -1538,32 +1527,32 @@ function ne(o, e) {
1538
1527
  }
1539
1528
  const re = "0.1.0";
1540
1529
  export {
1541
- V as AdManager,
1530
+ _ as AdManager,
1542
1531
  x as AnalyticsSender,
1543
- T as ClientInfoCollector,
1544
- y as DOMRenderer,
1532
+ S as ClientInfoCollector,
1533
+ m as DOMRenderer,
1545
1534
  w as HTMLRenderer,
1546
1535
  re as SDK_VERSION,
1547
- P as SessionManager,
1536
+ E as SessionManager,
1548
1537
  G as ViewabilityTracker,
1549
1538
  se as adaptAdToKoahAd,
1550
1539
  ee as clearClientInfoCache,
1551
1540
  Q as createAnalytics,
1552
1541
  J as createSession,
1553
- H as fetchAds,
1542
+ D as fetchAds,
1554
1543
  z as generateViewToken,
1555
1544
  Z as getAppInfo,
1556
- b as getClientInfo,
1557
- $ as getClientInfoCollector,
1545
+ y as getClientInfo,
1546
+ T as getClientInfoCollector,
1558
1547
  te as getClientInfoJSON,
1559
1548
  ie as getClientInfoSummary,
1560
1549
  X as getDeviceInfo,
1561
1550
  Y as getGeoInfo,
1562
- m as getSessionId,
1563
- D as getUserId,
1551
+ b as getSessionId,
1552
+ H as getUserId,
1564
1553
  N as getUserInfo,
1565
1554
  ne as getViewabilityTracker,
1566
- S as trackAdClick,
1555
+ O as trackAdClick,
1567
1556
  I as trackAdImpression,
1568
1557
  W as trackClicksBatch,
1569
1558
  j as trackImpressionsBatch
package/dist/index.umd.js CHANGED
@@ -1,4 +1,4 @@
1
- (function(l,v){typeof exports=="object"&&typeof module<"u"?v(exports):typeof define=="function"&&define.amd?define(["exports"],v):(l=typeof globalThis<"u"?globalThis:l||self,v(l.ActionXAdSDK={}))})(this,function(l){"use strict";class v{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){console.error(`[EventEmitter] Error in event handler for "${e}":`,n)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class k{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var g;const{variant:i="horizontal",includeWrapper:n=!0}=t,s=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(g=s.image)!=null&&g.url?`<img
1
+ (function(l,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(l=typeof globalThis<"u"?globalThis:l||self,k(l.ActionXAdSDK={}))})(this,function(l){"use strict";class k{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){console.error(`[EventEmitter] Error in event handler for "${e}":`,n)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class w{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var g;const{variant:i="horizontal",includeWrapper:n=!0}=t,s=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(g=s.image)!=null&&g.url?`<img
2
2
  src="${s.image.url}"
3
3
  alt="${s.title}"
4
4
  class="ax-ad-image"
@@ -17,7 +17,7 @@
17
17
  rel="sponsored noopener noreferrer"
18
18
  data-ad-id="${e.original.id}"
19
19
  data-impression-url="${a}"
20
- >`:"",w=n?"</a>":"";return f+h+w}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
20
+ >`:"",b=n?"</a>":"";return f+h+b}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
21
21
  <div class="ax-ad-suffix" data-ad-id="${e.original.id}">
22
22
  <div class="ax-ad-suffix-content">
23
23
  ${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
@@ -69,8 +69,8 @@
69
69
  </a>
70
70
  </div>
71
71
  </div>
72
- `}static renderAds(e,t=k.renderActionCard.bind(k)){return e.map(t).join(`
73
- `)}}const D="ad_session_id";function H(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function F(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function E(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class U{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||D,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const G=new U,m=()=>G.getSessionId();class A{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let n=null;for(let s=0;s<=this.retryAttempts;s++){const r=this.debug||F();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${s+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),u={"Content-Type":"application/json"},d=H();d&&(u["X-API-Key"]=d,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const h=await fetch(e,{method:"POST",headers:u,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!h.ok)throw new Error(`HTTP ${h.status}: ${h.statusText}`);const f=await h.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,f),f}catch(a){n=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${s+1}):`,a),s<this.retryAttempts&&await this.delay(this.retryDelay*(s+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,n),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const P=new A,I=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackImpression(o):P.trackImpression(o),x=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackClick(o):P.trackClick(o);function j(o,e){return`vt_${o}_${e}`}async function z(o){return Promise.all(o.map(e=>I(e)))}async function W(o){return Promise.all(o.map(e=>x(e)))}function Q(o){const e=new A(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function J(o={}){const e=new U(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const V=class V{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=k.renderActionCard(e,i);const n=t.querySelector(".ax-ad-card-link");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const n=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,n);const s=t.querySelector(".ax-ad-suffix-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const n=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,n);const s=t.querySelector(".ax-ad-source-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=k.renderLeadGenAd(e);const n=t.querySelector(".ax-ad-leadgen-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),n){t.innerHTML="";const s=document.createElement("div");return s.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,n),s.appendChild(a)}),t.appendChild(s),t}static handleClick(e,t){const{analytics:i,onClick:n}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}n&&n(e),i&&x({requestId:i.requestId,adId:e.original.id,destinationUrl:s,slotId:i.slotId,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,userId:i.userId},{baseUrl:i.apiBaseUrl}).catch(r=>{console.error("[DOMRenderer] Analytics click tracking failed:",r)}),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:n,onImpression:s}=i,r=n?`${n.requestId}:${n.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),u())},u=()=>{n?I({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:n.userId},{baseUrl:n.apiBaseUrl}).then(()=>{s&&s(e)}).catch(d=>{console.error("[DOMRenderer] Analytics impression tracking failed:",d)}):s&&s(e)};if(typeof IntersectionObserver<"u"){let d=!1,h=null;const f=new IntersectionObserver(g=>{g.forEach($=>{d=$.isIntersecting&&$.intersectionRatio>=.5,d?h||(h=setTimeout(()=>{h=null,d&&(c(),f.disconnect())},1e3)):h&&(clearTimeout(h),h=null)})},{threshold:.5}),w=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(w)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",n=e.adapted.body||"";return t==="inline"?`
72
+ `}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
73
+ `)}}const D="ad_session_id";function H(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function F(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function E(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class ${constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||D,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const G=new $,v=()=>G.getSessionId();class A{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let n=null;for(let s=0;s<=this.retryAttempts;s++){const r=this.debug||F();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${s+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),u={"Content-Type":"application/json"},d=H();d&&(u["X-API-Key"]=d,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const h=await fetch(e,{method:"POST",headers:u,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!h.ok)throw new Error(`HTTP ${h.status}: ${h.statusText}`);const f=await h.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,f),f}catch(a){n=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${s+1}):`,a),s<this.retryAttempts&&await this.delay(this.retryDelay*(s+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,n),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const P=new A,x=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackImpression(o):P.trackImpression(o),U=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackClick(o):P.trackClick(o);function j(o,e){return`vt_${o}_${e}`}async function z(o){return Promise.all(o.map(e=>x(e)))}async function W(o){return Promise.all(o.map(e=>U(e)))}function Q(o){const e=new A(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function J(o={}){const e=new $(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const V=class V{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=w.renderActionCard(e,i);const n=t.querySelector(".ax-ad-card-link");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const n=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,n);const s=t.querySelector(".ax-ad-suffix-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const n=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,n);const s=t.querySelector(".ax-ad-source-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=w.renderLeadGenAd(e);const n=t.querySelector(".ax-ad-leadgen-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),n){t.innerHTML="";const s=document.createElement("div");return s.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,n),s.appendChild(a)}),t.appendChild(s),t}static handleClick(e,t){const{onClick:i}=t,n=this.getClickUrl(e);if(!n){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}i&&i(e),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",n),window.open(n,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:n,onImpression:s}=i,r=n?`${n.requestId}:${n.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),u())},u=()=>{n?x({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:v(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:n.userId},{baseUrl:n.apiBaseUrl}).then(()=>{s&&s(e)}).catch(d=>{console.error("[DOMRenderer] Analytics impression tracking failed:",d)}):s&&s(e)};if(typeof IntersectionObserver<"u"){let d=!1,h=null;const f=new IntersectionObserver(g=>{g.forEach(C=>{d=C.isIntersecting&&C.intersectionRatio>=.5,d?h||(h=setTimeout(()=>{h=null,d&&(c(),f.disconnect())},1e3)):h&&(clearTimeout(h),h=null)})},{threshold:.5}),b=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(b)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",n=e.adapted.body||"";return t==="inline"?`
74
74
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
75
75
  ${i}
76
76
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
@@ -127,4 +127,4 @@
127
127
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
128
128
  </svg>
129
129
  </div>
130
- `}};V.trackedImpressionKeys=new Set;let y=V;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),n=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:n,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var n,s;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(n=document.querySelector('meta[property="og:title"]'))==null?void 0:n.getAttribute("content");if(t)return t;const i=(s=document.querySelector('meta[name="application-name"]'))==null?void 0:s.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const n=i.getAttribute("content");if(n)return n}}return"1.0.0"}inferPublisherId(){var i,n;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(n=window.location)==null?void 0:n.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const n=e.match(/Windows NT (\d+\.\d+)/);if(n)return n[1];const s=e.match(/Mac OS X (\d+[._]\d+)/);return s?s[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const n=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(n)return n[2];const s=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(s)return s[1]+" "+s[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const n=e.split("-")[1];if(n)return n.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(o){return T().collect(o)}function X(){return b().device}function O(){return b().user}function Z(){return b().app}function Y(){return b().geo}function B(){return O().id}function ee(){T().clearCache()}function te(o){const e=b(o);return JSON.stringify(e,null,2)}function ie(){var t;const o=b();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function q(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ne(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=T().collect(),s={...o,clientInfo:n},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),q()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",s));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(s)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(q()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},M={variant:"horizontal",count:1,preferences:{}};function R(o={}){const e={variant:o.variant??M.variant,count:o.count??M.count,preferences:{...M.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const p=class p{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new v,this.slots_config=R(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),n=p.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??m()}:{userId:this.currentUserId,sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((u,d)=>{const h=u.original.id;this.adsAnalyticsMap.set(h,{requestId:r.requestId,slotId:c.slotId,position:d,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,p.inFlightRequests.delete(i)}})();return p.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return B()}catch{return}}render(e,t,i={}){const n=typeof e!="string",s=n?p.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const u=a.adIndex??0,d=c.ads[u];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(d.original.id),f=this.slots_config.find($=>$.slotId===s),w=(f==null?void 0:f.format)||"action_card";r.innerHTML="";const g={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return w==="suffix"?y.renderSuffixAd(d,r,g):w==="source"?y.renderSponsoredSource(d,r,g):w==="lead_gen"?y.renderLeadGenAd(d,r,g):y.renderActionCard(d,r,g)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=R(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const n=this.adsAnalyticsMap.get(i);n&&t.set(i,n)}),t}async trackAdClick(e,t,i){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),r=await x({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:s,slotId:n.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,n.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),s=await I({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:n,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return s&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",s.eventId)),s}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let _=p;function K(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class N{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(u=>this.handleIntersection(e,u[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const c=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,n,s){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const u=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!r.enteredViewportAt&&(r.enteredViewportAt=u,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${u}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:u})),!d&&r.enteredViewportAt){const h=u-r.enteredViewportAt;r.totalVisibleTimeMs+=h,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:u})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,n){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),u=a.isViewable;if(a.enteredViewportAt){const d=c-a.enteredViewportAt;a.currentVisibleTimeMs=d,d>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,u||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var s,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const n=((s=this.config.batchConfig)==null?void 0:s.maxBatchSize)??5;if(i.length>=n)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,u=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,u)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=K();s&&(n["x-api-key"]=s),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:n,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(n){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,n),t.unshift(...i)}}endTracking(e,t,i,n){const s=this.metrics.get(e);if(!s)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"end_tracking",visiblePercentage:s.visiblePercentage,maxVisiblePercentage:s.maxVisiblePercentage,totalVisibleTimeMs:s.totalVisibleTimeMs,isViewable:s.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const n=this.timers.get(`${e}_monitoring`);n&&(clearInterval(n),this.timers.delete(`${e}_monitoring`));const s=this.batchTimers.get(e);s&&(clearTimeout(s),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},n=K();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let C=null;function re(o,e){return C||(C=new N(o,e),C.setupBeforeUnload()),C}const oe="0.1.0";l.AdManager=_,l.AnalyticsSender=A,l.ClientInfoCollector=S,l.DOMRenderer=y,l.HTMLRenderer=k,l.SDK_VERSION=oe,l.SessionManager=U,l.ViewabilityTracker=N,l.adaptAdToKoahAd=ne,l.clearClientInfoCache=ee,l.createAnalytics=Q,l.createSession=J,l.fetchAds=L,l.generateViewToken=j,l.getAppInfo=Z,l.getClientInfo=b,l.getClientInfoCollector=T,l.getClientInfoJSON=te,l.getClientInfoSummary=ie,l.getDeviceInfo=X,l.getGeoInfo=Y,l.getSessionId=m,l.getUserId=B,l.getUserInfo=O,l.getViewabilityTracker=re,l.trackAdClick=x,l.trackAdImpression=I,l.trackClicksBatch=W,l.trackImpressionsBatch=z,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
130
+ `}};V.trackedImpressionKeys=new Set;let m=V;class I{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),n=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:n,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var n,s;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(n=document.querySelector('meta[property="og:title"]'))==null?void 0:n.getAttribute("content");if(t)return t;const i=(s=document.querySelector('meta[name="application-name"]'))==null?void 0:s.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const n=i.getAttribute("content");if(n)return n}}return"1.0.0"}inferPublisherId(){var i,n;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(n=window.location)==null?void 0:n.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new I),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const n=e.match(/Windows NT (\d+\.\d+)/);if(n)return n[1];const s=e.match(/Mac OS X (\d+[._]\d+)/);return s?s[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const n=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(n)return n[2];const s=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(s)return s[1]+" "+s[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const n=e.split("-")[1];if(n)return n.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const S=()=>I.getInstance();function y(o){return S().collect(o)}function X(){return y().device}function O(){return y().user}function Z(){return y().app}function Y(){return y().geo}function B(){return O().id}function ee(){S().clearCache()}function te(o){const e=y(o);return JSON.stringify(e,null,2)}function ie(){var t;const o=y();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function q(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ne(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=S().collect(),s={...o,clientInfo:n},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),q()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",s));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(s)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(q()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},M={variant:"horizontal",count:1,preferences:{}};function R(o={}){const e={variant:o.variant??M.variant,count:o.count??M.count,preferences:{...M.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const p=class p{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new k,this.slots_config=R(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),n=p.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??v()}:{userId:this.currentUserId,sessionId:v()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((u,d)=>{const h=u.original.id;this.adsAnalyticsMap.set(h,{requestId:r.requestId,slotId:c.slotId,position:d,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,p.inFlightRequests.delete(i)}})();return p.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return B()}catch{return}}render(e,t,i={}){const n=typeof e!="string",s=n?p.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const u=a.adIndex??0,d=c.ads[u];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(d.original.id),f=this.slots_config.find(C=>C.slotId===s),b=(f==null?void 0:f.format)||"action_card";r.innerHTML="";const g={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return b==="suffix"?m.renderSuffixAd(d,r,g):b==="source"?m.renderSponsoredSource(d,r,g):b==="lead_gen"?m.renderLeadGenAd(d,r,g):m.renderActionCard(d,r,g)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=R(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const n=this.adsAnalyticsMap.get(i);n&&t.set(i,n)}),t}async trackAdClick(e,t,i){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=v(),r=await U({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:s,slotId:n.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,n.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=v(),s=await x({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:n,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return s&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",s.eventId)),s}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let _=p;function K(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class N{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(u=>this.handleIntersection(e,u[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const c=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,n,s){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const u=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!r.enteredViewportAt&&(r.enteredViewportAt=u,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${u}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:u})),!d&&r.enteredViewportAt){const h=u-r.enteredViewportAt;r.totalVisibleTimeMs+=h,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:u})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,n){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),u=a.isViewable;if(a.enteredViewportAt){const d=c-a.enteredViewportAt;a.currentVisibleTimeMs=d,d>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,u||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var s,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const n=((s=this.config.batchConfig)==null?void 0:s.maxBatchSize)??5;if(i.length>=n)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,u=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,u)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=K();s&&(n["x-api-key"]=s),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:n,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(n){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,n),t.unshift(...i)}}endTracking(e,t,i,n){const s=this.metrics.get(e);if(!s)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"end_tracking",visiblePercentage:s.visiblePercentage,maxVisiblePercentage:s.maxVisiblePercentage,totalVisibleTimeMs:s.totalVisibleTimeMs,isViewable:s.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const n=this.timers.get(`${e}_monitoring`);n&&(clearInterval(n),this.timers.delete(`${e}_monitoring`));const s=this.batchTimers.get(e);s&&(clearTimeout(s),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},n=K();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let T=null;function re(o,e){return T||(T=new N(o,e),T.setupBeforeUnload()),T}const oe="0.1.0";l.AdManager=_,l.AnalyticsSender=A,l.ClientInfoCollector=I,l.DOMRenderer=m,l.HTMLRenderer=w,l.SDK_VERSION=oe,l.SessionManager=$,l.ViewabilityTracker=N,l.adaptAdToKoahAd=ne,l.clearClientInfoCache=ee,l.createAnalytics=Q,l.createSession=J,l.fetchAds=L,l.generateViewToken=j,l.getAppInfo=Z,l.getClientInfo=y,l.getClientInfoCollector=S,l.getClientInfoJSON=te,l.getClientInfoSummary=ie,l.getDeviceInfo=X,l.getGeoInfo=Y,l.getSessionId=v,l.getUserId=B,l.getUserInfo=O,l.getViewabilityTracker=re,l.trackAdClick=U,l.trackAdImpression=x,l.trackClicksBatch=W,l.trackImpressionsBatch=z,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@action-x/ad-sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Zero-dependency ad SDK for ActionX",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",