@action-x/ad-sdk 0.1.8 → 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
@@ -1,28 +1,23 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});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(s){console.error(`[EventEmitter] Error in event handler for "${e}":`,s)}})}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 f;const{variant:i="horizontal",includeWrapper:s=!0}=t,n=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=s?`<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">`:"",d=(f=n.image)!=null&&f.url?`<img
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});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(s){console.error(`[EventEmitter] Error in event handler for "${e}":`,s)}})}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 f;const{variant:i="horizontal",includeWrapper:s=!0}=t,n=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(f=n.image)!=null&&f.url?`<img
2
2
  src="${n.image.url}"
3
3
  alt="${n.title}"
4
4
  class="ax-ad-image"
5
5
  loading="lazy"
6
- />`:"",l=(n.brand||"").trim(),u=l&&l.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${n.brand}</span>`:"",h=`
7
- ${d}
6
+ />`:"",d=(n.brand||"").trim(),l=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${n.brand}</span>`:"",u=`
7
+ ${c}
8
8
  <div class="ax-ad-content">
9
- ${u}
9
+ ${l}
10
10
  <h3 class="ax-ad-title">${n.title}</h3>
11
11
  ${n.body?`<p class="ax-ad-body">${n.body}</p>`:""}
12
- <div class="ax-ad-footer">
13
- <a
14
- href="${r}"
15
- class="ax-ad-cta"
16
- target="_blank"
17
- rel="sponsored noopener noreferrer"
18
- data-ad-id="${e.original.id}"
19
- data-impression-url="${a}"
20
- >
21
- ${this.getCtaText(e)}
22
- </a>
23
- </div>
24
12
  </div>
25
- `,p=s?"</div>":"";return c+h+p}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
13
+ `,h=s?`<a
14
+ href="${r}"
15
+ class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
16
+ target="_blank"
17
+ rel="sponsored noopener noreferrer"
18
+ data-ad-id="${e.original.id}"
19
+ data-impression-url="${a}"
20
+ >`:"",p=s?"</a>":"";return h+u+p}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
26
21
  <div class="ax-ad-suffix" data-ad-id="${e.original.id}">
27
22
  <div class="ax-ad-suffix-content">
28
23
  ${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
@@ -75,7 +70,7 @@
75
70
  </div>
76
71
  </div>
77
72
  `}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
78
- `)}}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 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),I=(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=>I(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 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-cta");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(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"?`
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"?`
79
74
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
80
75
  ${i}
81
76
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
@@ -132,4 +127,4 @@
132
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" />
133
128
  </svg>
134
129
  </div>
135
- `}};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(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"?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=v;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
@@ -1,4 +1,4 @@
1
- class O {
1
+ class B {
2
2
  constructor() {
3
3
  this.events = {};
4
4
  }
@@ -61,32 +61,27 @@ class w {
61
61
  */
62
62
  static renderActionCard(e, t = {}) {
63
63
  var f;
64
- const { variant: i = "horizontal", includeWrapper: s = !0 } = t, n = e.adapted, r = this.getClickUrl(e), a = this.getImpressionUrl(e), c = s ? `<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">` : "", d = (f = n.image) != null && f.url ? `<img
64
+ const { variant: i = "horizontal", includeWrapper: s = !0 } = t, n = e.adapted, r = this.getClickUrl(e), a = this.getImpressionUrl(e), c = (f = n.image) != null && f.url ? `<img
65
65
  src="${n.image.url}"
66
66
  alt="${n.title}"
67
67
  class="ax-ad-image"
68
68
  loading="lazy"
69
- />` : "", l = (n.brand || "").trim(), u = l && l.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${n.brand}</span>` : "", h = `
70
- ${d}
69
+ />` : "", u = (n.brand || "").trim(), l = u && u.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${n.brand}</span>` : "", d = `
70
+ ${c}
71
71
  <div class="ax-ad-content">
72
- ${u}
72
+ ${l}
73
73
  <h3 class="ax-ad-title">${n.title}</h3>
74
74
  ${n.body ? `<p class="ax-ad-body">${n.body}</p>` : ""}
75
- <div class="ax-ad-footer">
76
- <a
77
- href="${r}"
78
- class="ax-ad-cta"
79
- target="_blank"
80
- rel="sponsored noopener noreferrer"
81
- data-ad-id="${e.original.id}"
82
- data-impression-url="${a}"
83
- >
84
- ${this.getCtaText(e)}
85
- </a>
86
- </div>
87
75
  </div>
88
- `, p = s ? "</div>" : "";
89
- return c + h + p;
76
+ `, h = s ? `<a
77
+ href="${r}"
78
+ class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
79
+ target="_blank"
80
+ rel="sponsored noopener noreferrer"
81
+ data-ad-id="${e.original.id}"
82
+ data-impression-url="${a}"
83
+ >` : "", p = s ? "</a>" : "";
84
+ return h + d + p;
90
85
  }
91
86
  /**
92
87
  * Render Suffix ad as HTML string
@@ -195,7 +190,7 @@ function R() {
195
190
  var o;
196
191
  return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
197
192
  }
198
- function U(o) {
193
+ function C(o) {
199
194
  if (o) return o;
200
195
  if (typeof window < "u") {
201
196
  const e = window.__AD_CONFIG__;
@@ -204,7 +199,7 @@ function U(o) {
204
199
  }
205
200
  return "/api/v1";
206
201
  }
207
- class P {
202
+ class E {
208
203
  constructor(e = {}) {
209
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");
210
205
  }
@@ -240,7 +235,7 @@ class P {
240
235
  this.storage === "sessionStorage" ? sessionStorage.removeItem(this.sessionKey) : this.memoryCache.delete(this.sessionKey);
241
236
  }
242
237
  }
243
- const K = new P(), m = () => K.getSessionId();
238
+ const K = new E(), b = () => K.getSessionId();
244
239
  class x {
245
240
  constructor(e = {}) {
246
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;
@@ -250,7 +245,7 @@ class x {
250
245
  */
251
246
  async trackImpression(e) {
252
247
  return this.sendRequest(
253
- `${U(this.explicitBaseUrl)}/ads/impression`,
248
+ `${C(this.explicitBaseUrl)}/ads/impression`,
254
249
  e,
255
250
  "impression"
256
251
  );
@@ -260,7 +255,7 @@ class x {
260
255
  */
261
256
  async trackClick(e) {
262
257
  return this.sendRequest(
263
- `${U(this.explicitBaseUrl)}/ads/click`,
258
+ `${C(this.explicitBaseUrl)}/ads/click`,
264
259
  e,
265
260
  "click"
266
261
  );
@@ -275,20 +270,20 @@ class x {
275
270
  const r = this.debug || R();
276
271
  try {
277
272
  r && (console.log(`[Analytics] Request URL (${i}):`, e), console.log(`[Analytics] Sending ${i} event (attempt ${n + 1}):`, t));
278
- const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), d = {
273
+ const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), u = {
279
274
  "Content-Type": "application/json"
280
275
  }, l = L();
281
- l && (d["X-API-Key"] = l, r && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
282
- 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, {
283
278
  method: "POST",
284
- headers: d,
279
+ headers: u,
285
280
  body: JSON.stringify(t),
286
281
  keepalive: !0,
287
282
  signal: a.signal
288
283
  });
289
- if (clearTimeout(c), !u.ok)
290
- throw new Error(`HTTP ${u.status}: ${u.statusText}`);
291
- 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();
292
287
  return r && console.log(`[Analytics] ${i} event tracked successfully:`, h), h;
293
288
  } catch (a) {
294
289
  s = a, r && console.warn(`[Analytics] ${i} tracking failed (attempt ${n + 1}):`, a), n < this.retryAttempts && await this.delay(this.retryDelay * (n + 1));
@@ -303,7 +298,7 @@ class x {
303
298
  return new Promise((t) => setTimeout(t, e));
304
299
  }
305
300
  }
306
- const B = new x(), I = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackImpression(o) : B.trackImpression(o), T = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackClick(o) : B.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);
307
302
  function z(o, e) {
308
303
  return `vt_${o}_${e}`;
309
304
  }
@@ -311,7 +306,7 @@ async function j(o) {
311
306
  return Promise.all(o.map((e) => I(e)));
312
307
  }
313
308
  async function W(o) {
314
- return Promise.all(o.map((e) => T(e)));
309
+ return Promise.all(o.map((e) => O(e)));
315
310
  }
316
311
  function Q(o) {
317
312
  const e = new x(o);
@@ -321,14 +316,14 @@ function Q(o) {
321
316
  };
322
317
  }
323
318
  function J(o = {}) {
324
- const e = new P(o);
319
+ const e = new E(o);
325
320
  return {
326
321
  getSessionId: () => e.getSessionId(),
327
322
  regenerateSessionId: () => e.regenerateSessionId(),
328
323
  clearSessionId: () => e.clearSessionId()
329
324
  };
330
325
  }
331
- const C = class C {
326
+ const $ = class $ {
332
327
  static isDebugEnabled() {
333
328
  var e;
334
329
  return typeof window < "u" ? !!((e = window.__AD_CONFIG__) != null && e.debug) : !1;
@@ -344,7 +339,7 @@ const C = class C {
344
339
  */
345
340
  static renderActionCard(e, t, i = {}) {
346
341
  t.innerHTML = w.renderActionCard(e, i);
347
- const s = t.querySelector(".ax-ad-cta");
342
+ const s = t.querySelector(".ax-ad-card-link");
348
343
  return s && s.addEventListener("click", (n) => {
349
344
  n.preventDefault(), this.handleClick(e, i);
350
345
  }), this.trackImpression(e, t, i), t;
@@ -394,26 +389,15 @@ const C = class C {
394
389
  }
395
390
  /**
396
391
  * Handle ad click
397
- * First tracks the click (async, non-blocking), then opens the URL.
392
+ * Directly opens click_url in a new window.
398
393
  */
399
394
  static handleClick(e, t) {
400
- const { analytics: i, onClick: s } = t, n = this.getClickUrl(e);
401
- if (!n) {
395
+ const { onClick: i } = t, s = this.getClickUrl(e);
396
+ if (!s) {
402
397
  console.warn("[DOMRenderer] Missing click url for ad:", e.original.id);
403
398
  return;
404
399
  }
405
- s && s(e), i && T({
406
- requestId: i.requestId,
407
- adId: e.original.id,
408
- destinationUrl: n,
409
- slotId: i.slotId,
410
- sessionId: m(),
411
- adTitle: e.adapted.title,
412
- format: e.original.type,
413
- userId: i.userId
414
- }, { baseUrl: i.apiBaseUrl }).catch((r) => {
415
- console.error("[DOMRenderer] Analytics click tracking failed:", r);
416
- }), 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");
417
401
  }
418
402
  /**
419
403
  * Track ad impression using IntersectionObserver (50% visible + 1000ms delay)
@@ -425,15 +409,15 @@ const C = class C {
425
409
  return;
426
410
  let a = !1;
427
411
  const c = () => {
428
- a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), d());
429
- }, d = () => {
412
+ a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), u());
413
+ }, u = () => {
430
414
  s ? I({
431
415
  requestId: s.requestId,
432
416
  adId: e.original.id,
433
417
  slotId: s.slotId,
434
418
  position: s.position,
435
419
  totalAds: s.totalAds,
436
- sessionId: m(),
420
+ sessionId: b(),
437
421
  adTitle: e.adapted.title,
438
422
  format: e.original.type,
439
423
  source: "internal",
@@ -446,13 +430,13 @@ const C = class C {
446
430
  }) : n && n(e);
447
431
  };
448
432
  if (typeof IntersectionObserver < "u") {
449
- let l = !1, u = null;
433
+ let l = !1, d = null;
450
434
  const h = new IntersectionObserver(
451
435
  (f) => {
452
436
  f.forEach((v) => {
453
- l = v.isIntersecting && v.intersectionRatio >= 0.5, l ? u || (u = setTimeout(() => {
454
- u = null, l && (c(), h.disconnect());
455
- }, 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);
456
440
  });
457
441
  },
458
442
  { threshold: 0.5 }
@@ -534,8 +518,8 @@ const C = class C {
534
518
  `;
535
519
  }
536
520
  };
537
- C.trackedImpressionKeys = /* @__PURE__ */ new Set();
538
- let y = C;
521
+ $.trackedImpressionKeys = /* @__PURE__ */ new Set();
522
+ let m = $;
539
523
  class S {
540
524
  constructor() {
541
525
  this.cachedStaticInfo = null, this.cacheTimestamp = 0, this.CACHE_TTL = 36e5;
@@ -926,35 +910,35 @@ class S {
926
910
  this.cachedStaticInfo = null, this.cacheTimestamp = 0;
927
911
  }
928
912
  }
929
- const $ = () => S.getInstance();
930
- function b(o) {
931
- return $().collect(o);
913
+ const T = () => S.getInstance();
914
+ function y(o) {
915
+ return T().collect(o);
932
916
  }
933
917
  function X() {
934
- return b().device;
918
+ return y().device;
935
919
  }
936
920
  function N() {
937
- return b().user;
921
+ return y().user;
938
922
  }
939
923
  function Z() {
940
- return b().app;
924
+ return y().app;
941
925
  }
942
926
  function Y() {
943
- return b().geo;
927
+ return y().geo;
944
928
  }
945
- function D() {
929
+ function H() {
946
930
  return N().id;
947
931
  }
948
932
  function ee() {
949
- $().clearCache();
933
+ T().clearCache();
950
934
  }
951
935
  function te(o) {
952
- const e = b(o);
936
+ const e = y(o);
953
937
  return JSON.stringify(e, null, 2);
954
938
  }
955
939
  function ie() {
956
940
  var t;
957
- const o = b();
941
+ const o = y();
958
942
  return [
959
943
  o.device.os,
960
944
  o.device.osv,
@@ -963,7 +947,7 @@ function ie() {
963
947
  ((t = o.geo) == null ? void 0 : t.country) || "Unknown"
964
948
  ].join(" / ");
965
949
  }
966
- function M() {
950
+ function U() {
967
951
  var o;
968
952
  return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
969
953
  }
@@ -994,15 +978,15 @@ function se(o) {
994
978
  }
995
979
  };
996
980
  }
997
- async function H(o, e = {}) {
998
- 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 = {
999
983
  ...o,
1000
984
  clientInfo: s
1001
985
  // Auto-injected
1002
986
  }, r = {
1003
987
  "Content-Type": "application/json"
1004
988
  };
1005
- 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));
1006
990
  const a = await fetch(`${t}/ads/request`, {
1007
991
  method: "POST",
1008
992
  headers: r,
@@ -1011,7 +995,7 @@ async function H(o, e = {}) {
1011
995
  if (!a.ok)
1012
996
  throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);
1013
997
  const c = await a.json();
1014
- if (M() && console.log("[fetchAds] Response:", c), !c.success)
998
+ if (U() && console.log("[fetchAds] Response:", c), !c.success)
1015
999
  throw new Error(c.error || "Ad request failed");
1016
1000
  return c.data;
1017
1001
  }
@@ -1020,7 +1004,7 @@ const F = { width: 400, height: 200 }, A = {
1020
1004
  count: 1,
1021
1005
  preferences: {}
1022
1006
  };
1023
- function _(o = {}) {
1007
+ function M(o = {}) {
1024
1008
  const e = {
1025
1009
  variant: o.variant ?? A.variant,
1026
1010
  count: o.count ?? A.count,
@@ -1041,7 +1025,7 @@ function _(o = {}) {
1041
1025
  }
1042
1026
  const g = class g {
1043
1027
  constructor(e) {
1044
- this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new O(), 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__ = {
1045
1029
  ...window.__AD_CONFIG__ || {},
1046
1030
  apiKey: e.apiKey,
1047
1031
  apiBaseUrl: e.apiBaseUrl,
@@ -1065,14 +1049,14 @@ const g = class g {
1065
1049
  this.isLoading = !0, this.eventBus.emit("adsLoading"), this.config.debug && console.log("[AdManager] Starting ad request...");
1066
1050
  const n = (async () => {
1067
1051
  try {
1068
- const r = await H(
1052
+ const r = await D(
1069
1053
  {
1070
1054
  conversationContext: t.conversationContext,
1071
1055
  userContext: t.userContext ? {
1072
1056
  ...t.userContext,
1073
1057
  userId: t.userContext.userId ?? this.currentUserId,
1074
- sessionId: t.userContext.sessionId ?? m()
1075
- } : { userId: this.currentUserId, sessionId: m() },
1058
+ sessionId: t.userContext.sessionId ?? b()
1059
+ } : { userId: this.currentUserId, sessionId: b() },
1076
1060
  slots: this.slots_config
1077
1061
  },
1078
1062
  {
@@ -1081,9 +1065,9 @@ const g = class g {
1081
1065
  }
1082
1066
  );
1083
1067
  this.currentRequestId = r.requestId, this.adsAnalyticsMap.clear(), r.slots.forEach((c) => {
1084
- c.ads.forEach((d, l) => {
1085
- const u = d.original.id;
1086
- this.adsAnalyticsMap.set(u, {
1068
+ c.ads.forEach((u, l) => {
1069
+ const d = u.original.id;
1070
+ this.adsAnalyticsMap.set(d, {
1087
1071
  requestId: r.requestId,
1088
1072
  slotId: c.slotId,
1089
1073
  position: l,
@@ -1122,7 +1106,7 @@ const g = class g {
1122
1106
  if (e != null && e.userId)
1123
1107
  return e.userId;
1124
1108
  try {
1125
- return D();
1109
+ return H();
1126
1110
  } catch {
1127
1111
  return;
1128
1112
  }
@@ -1134,18 +1118,18 @@ const g = class g {
1134
1118
  const c = this.slots[n];
1135
1119
  if (!c || !c.ads || c.ads.length === 0)
1136
1120
  return this.config.debug && console.warn("[AdManager] No ads in slot:", n), null;
1137
- const d = a.adIndex ?? 0, l = c.ads[d];
1121
+ const u = a.adIndex ?? 0, l = c.ads[u];
1138
1122
  if (!l)
1139
- return this.config.debug && console.warn(`[AdManager] Ad at index ${d} not found in slot:`, n), null;
1140
- 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";
1141
1125
  r.innerHTML = "";
1142
1126
  const f = {
1143
- analytics: u,
1127
+ analytics: d,
1144
1128
  variant: a.variant,
1145
1129
  onClick: a.onClick,
1146
1130
  onImpression: a.onImpression
1147
1131
  };
1148
- 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);
1149
1133
  }
1150
1134
  /**
1151
1135
  * Get ad slots data
@@ -1184,7 +1168,7 @@ const g = class g {
1184
1168
  * Update configuration
1185
1169
  */
1186
1170
  updateConfig(e) {
1187
- 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);
1188
1172
  }
1189
1173
  /**
1190
1174
  * Clear all slot data
@@ -1239,7 +1223,7 @@ const g = class g {
1239
1223
  const s = this.adsAnalyticsMap.get(e);
1240
1224
  if (!s || !this.currentRequestId)
1241
1225
  return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
1242
- const n = m(), r = await T({
1226
+ const n = b(), r = await O({
1243
1227
  requestId: this.currentRequestId,
1244
1228
  adId: e,
1245
1229
  destinationUrl: t,
@@ -1259,7 +1243,7 @@ const g = class g {
1259
1243
  const i = this.adsAnalyticsMap.get(e);
1260
1244
  if (!i || !this.currentRequestId)
1261
1245
  return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
1262
- const s = m(), n = await I({
1246
+ const s = b(), n = await I({
1263
1247
  requestId: this.currentRequestId,
1264
1248
  adId: e,
1265
1249
  slotId: i.slotId,
@@ -1282,8 +1266,8 @@ const g = class g {
1282
1266
  }
1283
1267
  };
1284
1268
  g.DEFAULT_SLOT_ID = "action_card", g.inFlightRequests = /* @__PURE__ */ new Map();
1285
- let V = g;
1286
- function E() {
1269
+ let _ = g;
1270
+ function V() {
1287
1271
  if (typeof window < "u") {
1288
1272
  const o = window.__AD_CONFIG__;
1289
1273
  if (o != null && o.apiKey)
@@ -1325,7 +1309,7 @@ class G {
1325
1309
  };
1326
1310
  this.metrics.set(e, r), this.isTracking.set(e, !0), this.eventQueue.set(e, []);
1327
1311
  const a = new IntersectionObserver(
1328
- (d) => this.handleIntersection(e, d[0], i, s, n),
1312
+ (u) => this.handleIntersection(e, u[0], i, s, n),
1329
1313
  {
1330
1314
  threshold: this.createThresholds()
1331
1315
  }
@@ -1357,8 +1341,8 @@ class G {
1357
1341
  if (!r || !this.isTracking.get(e)) return;
1358
1342
  const a = r.isViewable, c = Math.round(t.intersectionRatio * 100);
1359
1343
  r.visiblePercentage = c, r.maxVisiblePercentage = Math.max(r.maxVisiblePercentage, c);
1360
- const d = Date.now(), l = c >= this.config.minVisiblePercentage;
1361
- 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, {
1362
1346
  adId: e,
1363
1347
  sessionId: i,
1364
1348
  requestId: s,
@@ -1368,10 +1352,10 @@ class G {
1368
1352
  maxVisiblePercentage: r.maxVisiblePercentage,
1369
1353
  totalVisibleTimeMs: r.totalVisibleTimeMs,
1370
1354
  isViewable: !1,
1371
- timestamp: d
1355
+ timestamp: u
1372
1356
  })), !l && r.enteredViewportAt) {
1373
- const u = d - r.enteredViewportAt;
1374
- 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, {
1375
1359
  adId: e,
1376
1360
  sessionId: i,
1377
1361
  requestId: s,
@@ -1381,7 +1365,7 @@ class G {
1381
1365
  maxVisiblePercentage: r.maxVisiblePercentage,
1382
1366
  totalVisibleTimeMs: r.totalVisibleTimeMs,
1383
1367
  isViewable: r.isViewable,
1384
- timestamp: d
1368
+ timestamp: u
1385
1369
  });
1386
1370
  }
1387
1371
  r.isViewable !== a && r.isViewable && this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`), this.metrics.set(e, r);
@@ -1397,10 +1381,10 @@ class G {
1397
1381
  clearInterval(r);
1398
1382
  return;
1399
1383
  }
1400
- const c = Date.now(), d = a.isViewable;
1384
+ const c = Date.now(), u = a.isViewable;
1401
1385
  if (a.enteredViewportAt) {
1402
1386
  const l = c - a.enteredViewportAt;
1403
- 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, {
1404
1388
  adId: e,
1405
1389
  sessionId: t,
1406
1390
  requestId: i,
@@ -1434,10 +1418,10 @@ class G {
1434
1418
  else {
1435
1419
  const a = this.batchTimers.get(e);
1436
1420
  a && clearTimeout(a);
1437
- 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(() => {
1438
1422
  this.flushQueue(e);
1439
1423
  }, c);
1440
- this.batchTimers.set(e, d);
1424
+ this.batchTimers.set(e, u);
1441
1425
  }
1442
1426
  }
1443
1427
  /**
@@ -1451,7 +1435,7 @@ class G {
1451
1435
  try {
1452
1436
  const s = {
1453
1437
  "Content-Type": "application/json"
1454
- }, n = E();
1438
+ }, n = V();
1455
1439
  n && (s["x-api-key"] = n), await fetch(`${this.baseUrl}/ads/viewability/batch`, {
1456
1440
  method: "POST",
1457
1441
  headers: s,
@@ -1525,7 +1509,7 @@ class G {
1525
1509
  if (t && t.length > 0) {
1526
1510
  const i = {
1527
1511
  "Content-Type": "application/json"
1528
- }, s = E();
1512
+ }, s = V();
1529
1513
  s && (i["x-api-key"] = s), fetch(`${this.baseUrl}/ads/viewability/batch`, {
1530
1514
  method: "POST",
1531
1515
  headers: i,
@@ -1543,32 +1527,32 @@ function ne(o, e) {
1543
1527
  }
1544
1528
  const re = "0.1.0";
1545
1529
  export {
1546
- V as AdManager,
1530
+ _ as AdManager,
1547
1531
  x as AnalyticsSender,
1548
1532
  S as ClientInfoCollector,
1549
- y as DOMRenderer,
1533
+ m as DOMRenderer,
1550
1534
  w as HTMLRenderer,
1551
1535
  re as SDK_VERSION,
1552
- P as SessionManager,
1536
+ E as SessionManager,
1553
1537
  G as ViewabilityTracker,
1554
1538
  se as adaptAdToKoahAd,
1555
1539
  ee as clearClientInfoCache,
1556
1540
  Q as createAnalytics,
1557
1541
  J as createSession,
1558
- H as fetchAds,
1542
+ D as fetchAds,
1559
1543
  z as generateViewToken,
1560
1544
  Z as getAppInfo,
1561
- b as getClientInfo,
1562
- $ as getClientInfoCollector,
1545
+ y as getClientInfo,
1546
+ T as getClientInfoCollector,
1563
1547
  te as getClientInfoJSON,
1564
1548
  ie as getClientInfoSummary,
1565
1549
  X as getDeviceInfo,
1566
1550
  Y as getGeoInfo,
1567
- m as getSessionId,
1568
- D as getUserId,
1551
+ b as getSessionId,
1552
+ H as getUserId,
1569
1553
  N as getUserInfo,
1570
1554
  ne as getViewabilityTracker,
1571
- T as trackAdClick,
1555
+ O as trackAdClick,
1572
1556
  I as trackAdImpression,
1573
1557
  W as trackClicksBatch,
1574
1558
  j as trackImpressionsBatch
package/dist/index.umd.js CHANGED
@@ -1,28 +1,23 @@
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 v{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=n?`<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">`:"",u=(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"
5
5
  loading="lazy"
6
- />`:"",d=(s.brand||"").trim(),h=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${s.brand}</span>`:"",f=`
7
- ${u}
6
+ />`:"",u=(s.brand||"").trim(),d=u&&u.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${s.brand}</span>`:"",h=`
7
+ ${c}
8
8
  <div class="ax-ad-content">
9
- ${h}
9
+ ${d}
10
10
  <h3 class="ax-ad-title">${s.title}</h3>
11
11
  ${s.body?`<p class="ax-ad-body">${s.body}</p>`:""}
12
- <div class="ax-ad-footer">
13
- <a
14
- href="${r}"
15
- class="ax-ad-cta"
16
- target="_blank"
17
- rel="sponsored noopener noreferrer"
18
- data-ad-id="${e.original.id}"
19
- data-impression-url="${a}"
20
- >
21
- ${this.getCtaText(e)}
22
- </a>
23
- </div>
24
12
  </div>
25
- `,w=n?"</div>":"";return c+f+w}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
13
+ `,f=n?`<a
14
+ href="${r}"
15
+ class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
16
+ target="_blank"
17
+ rel="sponsored noopener noreferrer"
18
+ data-ad-id="${e.original.id}"
19
+ data-impression-url="${a}"
20
+ >`:"",b=n?"</a>":"";return f+h+b}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
26
21
  <div class="ax-ad-suffix" data-ad-id="${e.original.id}">
27
22
  <div class="ax-ad-suffix-content">
28
23
  ${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
@@ -74,8 +69,8 @@
74
69
  </a>
75
70
  </div>
76
71
  </div>
77
- `}static renderAds(e,t=v.renderActionCard.bind(v)){return e.map(t).join(`
78
- `)}}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=v.renderActionCard(e,i);const n=t.querySelector(".ax-ad-cta");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=v.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"?`
79
74
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
80
75
  ${i}
81
76
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
@@ -132,4 +127,4 @@
132
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" />
133
128
  </svg>
134
129
  </div>
135
- `}};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 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??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=v,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/dist/style.css CHANGED
@@ -1 +1 @@
1
- .ax-ad-card{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px #0000001a;background:#fff}.ax-ad-card a{text-decoration:none;color:inherit}.ax-ad-card-horizontal{display:flex;flex-direction:row;align-items:stretch}.ax-ad-card-vertical{display:flex;flex-direction:column}.ax-ad-image{width:100%;height:auto;object-fit:cover}.ax-ad-card-horizontal .ax-ad-image{width:132px;height:132px;flex-shrink:0}.ax-ad-card-vertical .ax-ad-image{width:100%;aspect-ratio:16 / 9;min-height:150px}.ax-ad-content{padding:12px;flex:1;display:flex;flex-direction:column;min-width:0}.ax-ad-title{font-size:15px;font-weight:600;margin:0 0 6px;color:#111827;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.ax-ad-body{font-size:13px;color:#6b7280;margin:0;line-height:1.45;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.ax-ad-price{font-weight:600;color:#059669;margin-right:8px}.ax-ad-brand{font-size:12px;color:#9ca3af;text-transform:uppercase;margin-bottom:4px}.ax-ad-rating{color:#f59e0b;font-size:14px;margin:4px 0}.ax-ad-footer{display:flex;align-items:center;justify-content:flex-end;margin-top:auto;padding-top:10px}.ax-ad-cta{padding:7px 14px;background:#3b82f6;color:#fff;border-radius:6px;font-size:13px;font-weight:500;transition:background .2s;text-decoration:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.ax-ad-cta:hover{background:#2563eb}.ax-ad-suffix{border-top:1px solid #e5e7eb;padding:16px 0}.ax-ad-suffix-content{display:flex;justify-content:space-between;align-items:center}.ax-ad-suffix-title{margin:0;font-size:14px;font-weight:500;color:#374151}.ax-ad-suffix-body{font-size:12px;color:#6b7280;margin:4px 0 0}.ax-ad-suffix-link{color:#3b82f6;font-weight:500;text-decoration:none;cursor:pointer}.ax-ad-suffix-link:hover{text-decoration:underline}.ax-ad-variant-block{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb}.ax-ad-variant-block .ax-ad-suffix-text{font-size:14px;color:#374151;flex:1}.ax-ad-variant-block .ax-ad-suffix-badge{font-size:10px;color:#9ca3af;text-transform:uppercase;flex-shrink:0}.ax-ad-variant-inline{display:inline-flex;align-items:center;gap:4px;color:#3b82f6;font-weight:500;cursor:pointer}.ax-ad-variant-minimal{display:inline;color:#3b82f6;font-weight:500;cursor:pointer}.ax-ad-source-link{display:flex;align-items:center;gap:8px;padding:8px 0;text-decoration:none;cursor:pointer}.ax-ad-source-icon{width:24px;height:24px;border-radius:4px;object-fit:cover}.ax-ad-source-info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.ax-ad-source-name{font-weight:500;color:#111827;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-ad-source-desc{font-size:12px;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-ad-source-badge{font-size:10px;color:#9ca3af;text-transform:uppercase;flex-shrink:0}.ax-ad-variant-card{display:flex;align-items:center;gap:12px;padding:12px;border:1px solid #e5e7eb;border-radius:8px;background:#fff;cursor:pointer}.ax-ad-variant-card:hover{background:#f9fafb}.ax-ad-variant-list-item{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid #f3f4f6;cursor:pointer}.ax-ad-variant-list-item img{width:32px;height:32px;border-radius:4px;object-fit:cover;flex-shrink:0}.ax-ad-variant-minimal{display:inline-flex;align-items:center;gap:4px;cursor:pointer}.ax-ad-leadgen{border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;background:#fff}.ax-ad-leadgen-content{padding:16px}.ax-ad-leadgen-title{font-size:18px;font-weight:600;margin:0 0 8px;color:#111827}.ax-ad-leadgen-body{font-size:14px;color:#6b7280;margin:0 0 16px;line-height:1.5}.ax-ad-leadgen-cta{display:inline-block;padding:10px 20px;background:#3b82f6;color:#fff;border-radius:6px;font-weight:500;text-decoration:none;transition:background .2s;cursor:pointer}.ax-ad-leadgen-cta:hover{background:#2563eb}.ax-ads-container{display:flex;flex-direction:column;gap:12px}.ax-ad-wrapper{width:100%}.ax-ad-sponsored-badge{display:inline-flex;align-items:center;padding:2px 6px;background:#f3f4f6;border-radius:4px;font-size:10px;color:#9ca3af;text-transform:uppercase;letter-spacing:.05em;flex-shrink:0}
1
+ .ax-ad-card{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px #0000001a;background:#fff}.ax-ad-card a{text-decoration:none;color:inherit}.ax-ad-card-link{text-decoration:none;color:inherit;cursor:pointer;transition:box-shadow .2s ease,transform .2s ease,border-color .2s ease}.ax-ad-card-link:hover{border-color:#d1d5db;box-shadow:0 6px 20px #1118271f;transform:translateY(-1px)}.ax-ad-card-horizontal{display:flex;flex-direction:row;align-items:stretch}.ax-ad-card-vertical{display:flex;flex-direction:column}.ax-ad-image{width:100%;height:auto;object-fit:cover}.ax-ad-card-horizontal .ax-ad-image{width:132px;height:132px;flex-shrink:0}.ax-ad-card-vertical .ax-ad-image{width:100%;aspect-ratio:16 / 9;min-height:150px}.ax-ad-content{padding:12px;flex:1;display:flex;flex-direction:column;min-width:0}.ax-ad-title{font-size:15px;font-weight:600;margin:0 0 6px;color:#111827;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.ax-ad-body{font-size:13px;color:#6b7280;margin:0;line-height:1.45;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.ax-ad-price{font-weight:600;color:#059669;margin-right:8px}.ax-ad-brand{font-size:12px;color:#9ca3af;text-transform:uppercase;margin-bottom:4px}.ax-ad-rating{color:#f59e0b;font-size:14px;margin:4px 0}.ax-ad-footer{display:flex;align-items:center;justify-content:flex-end;margin-top:auto;padding-top:10px}.ax-ad-cta{padding:7px 14px;background:#3b82f6;color:#fff;border-radius:6px;font-size:13px;font-weight:500;transition:background .2s;text-decoration:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.ax-ad-cta:hover{background:#2563eb}.ax-ad-suffix{border-top:1px solid #e5e7eb;padding:16px 0}.ax-ad-suffix-content{display:flex;justify-content:space-between;align-items:center}.ax-ad-suffix-title{margin:0;font-size:14px;font-weight:500;color:#374151}.ax-ad-suffix-body{font-size:12px;color:#6b7280;margin:4px 0 0}.ax-ad-suffix-link{color:#3b82f6;font-weight:500;text-decoration:none;cursor:pointer}.ax-ad-suffix-link:hover{text-decoration:underline}.ax-ad-variant-block{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb}.ax-ad-variant-block .ax-ad-suffix-text{font-size:14px;color:#374151;flex:1}.ax-ad-variant-block .ax-ad-suffix-badge{font-size:10px;color:#9ca3af;text-transform:uppercase;flex-shrink:0}.ax-ad-variant-inline{display:inline-flex;align-items:center;gap:4px;color:#3b82f6;font-weight:500;cursor:pointer}.ax-ad-variant-minimal{display:inline;color:#3b82f6;font-weight:500;cursor:pointer}.ax-ad-source-link{display:flex;align-items:center;gap:8px;padding:8px 0;text-decoration:none;cursor:pointer}.ax-ad-source-icon{width:24px;height:24px;border-radius:4px;object-fit:cover}.ax-ad-source-info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.ax-ad-source-name{font-weight:500;color:#111827;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-ad-source-desc{font-size:12px;color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-ad-source-badge{font-size:10px;color:#9ca3af;text-transform:uppercase;flex-shrink:0}.ax-ad-variant-card{display:flex;align-items:center;gap:12px;padding:12px;border:1px solid #e5e7eb;border-radius:8px;background:#fff;cursor:pointer}.ax-ad-variant-card:hover{background:#f9fafb}.ax-ad-variant-list-item{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid #f3f4f6;cursor:pointer}.ax-ad-variant-list-item img{width:32px;height:32px;border-radius:4px;object-fit:cover;flex-shrink:0}.ax-ad-variant-minimal{display:inline-flex;align-items:center;gap:4px;cursor:pointer}.ax-ad-leadgen{border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;background:#fff}.ax-ad-leadgen-content{padding:16px}.ax-ad-leadgen-title{font-size:18px;font-weight:600;margin:0 0 8px;color:#111827}.ax-ad-leadgen-body{font-size:14px;color:#6b7280;margin:0 0 16px;line-height:1.5}.ax-ad-leadgen-cta{display:inline-block;padding:10px 20px;background:#3b82f6;color:#fff;border-radius:6px;font-weight:500;text-decoration:none;transition:background .2s;cursor:pointer}.ax-ad-leadgen-cta:hover{background:#2563eb}.ax-ads-container{display:flex;flex-direction:column;gap:12px}.ax-ad-wrapper{width:100%}.ax-ad-sponsored-badge{display:inline-flex;align-items:center;padding:2px 6px;background:#f3f4f6;border-radius:4px;font-size:10px;color:#9ca3af;text-transform:uppercase;letter-spacing:.05em;flex-shrink:0}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@action-x/ad-sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Zero-dependency ad SDK for ActionX",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",