@action-x/ad-sdk 0.1.11 → 0.1.13

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,31 +1,31 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class N{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}}function b(r){return r.replace(/<[^>]+>/g," ").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&nbsp;/g," ").replace(/\s{2,}/g," ").trim()}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 f;const{variant:i="horizontal",includeWrapper:s=!0}=t,n=e.adapted,o=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(f=n.image)!=null&&f.url?`<div class="ax-ad-image-wrap">
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class N{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 n=this.events[e].indexOf(t);n>-1&&this.events[e].splice(n,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(n=>{try{n(...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}}function I(r){return r.replace(/<[^>]+>/g," ").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&nbsp;/g," ").replace(/\s{2,}/g," ").trim()}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 h;const{variant:n="horizontal",includeWrapper:s=!0}=t,i=e.adapted,o=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(h=i.image)!=null&&h.url?`<div class="ax-ad-image-wrap">
2
2
  <img
3
- src="${n.image.url}"
4
- alt="${n.title}"
3
+ src="${i.image.url}"
4
+ alt="${i.title}"
5
5
  class="ax-ad-image"
6
6
  loading="lazy"
7
7
  />
8
- </div>`:"",d=(n.brand||"").trim(),l=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${n.brand}</span>`:"",u=`
8
+ </div>`:"",u=(i.brand||"").trim(),l=u&&u.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${i.brand}</span>`:"",d=`
9
9
  ${c}
10
10
  <div class="ax-ad-content">
11
11
  ${l}
12
- <h3 class="ax-ad-title">${n.title}</h3>
13
- ${n.body?`<p class="ax-ad-body">${b(n.body)}</p>`:""}
12
+ <h3 class="ax-ad-title">${i.title}</h3>
13
+ ${i.body?`<p class="ax-ad-body">${I(i.body)}</p>`:""}
14
14
  </div>
15
- `,h=s?`<a
15
+ `,f=s?`<a
16
16
  href="${o}"
17
- class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
17
+ class="ax-ad-card ax-ad-card-${n} ax-ad-card-link"
18
18
  target="_blank"
19
19
  rel="sponsored noopener noreferrer"
20
20
  data-ad-id="${e.original.id}"
21
21
  data-impression-url="${a}"
22
- >`:"",p=s?"</a>":"";return h+u+p}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
22
+ >`:"",g=s?"</a>":"";return f+d+g}static renderSuffixAd(e){const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
23
23
  <div class="ax-ad-suffix" data-ad-id="${e.original.id}">
24
24
  <div class="ax-ad-suffix-content">
25
25
  ${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
26
- ${t.body?`<p class="ax-ad-suffix-body">${b(t.body)}</p>`:""}
26
+ ${t.body?`<p class="ax-ad-suffix-body">${I(t.body)}</p>`:""}
27
27
  <a
28
- href="${i}"
28
+ href="${n}"
29
29
  class="ax-ad-suffix-link"
30
30
  target="_blank"
31
31
  rel="sponsored noopener noreferrer"
@@ -36,31 +36,31 @@
36
36
  </a>
37
37
  </div>
38
38
  </div>
39
- `}static renderSponsoredSource(e){var n;const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
39
+ `}static renderSponsoredSource(e){var i;const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
40
40
  <div class="ax-ad-source" data-ad-id="${e.original.id}">
41
41
  <a
42
- href="${i}"
42
+ href="${n}"
43
43
  class="ax-ad-source-link"
44
44
  target="_blank"
45
45
  rel="sponsored noopener noreferrer"
46
46
  data-ad-id="${e.original.id}"
47
47
  data-impression-url="${s}"
48
48
  >
49
- ${(n=t.image)!=null&&n.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
49
+ ${(i=t.image)!=null&&i.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
50
50
  <div class="ax-ad-source-info">
51
51
  <span class="ax-ad-source-name">${t.title}</span>
52
- ${t.body?`<span class="ax-ad-source-desc">${b(t.body)}</span>`:""}
52
+ ${t.body?`<span class="ax-ad-source-desc">${I(t.body)}</span>`:""}
53
53
  </div>
54
54
  <span class="ax-ad-source-badge">Sponsored</span>
55
55
  </a>
56
56
  </div>
57
- `}static renderLeadGenAd(e){const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
57
+ `}static renderLeadGenAd(e){const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
58
58
  <div class="ax-ad-leadgen" data-ad-id="${e.original.id}">
59
59
  <div class="ax-ad-leadgen-content">
60
60
  ${t.title?`<h3 class="ax-ad-leadgen-title">${t.title}</h3>`:""}
61
- ${t.body?`<p class="ax-ad-leadgen-body">${b(t.body)}</p>`:""}
61
+ ${t.body?`<p class="ax-ad-leadgen-body">${I(t.body)}</p>`:""}
62
62
  <a
63
- href="${i}"
63
+ href="${n}"
64
64
  class="ax-ad-leadgen-cta"
65
65
  target="_blank"
66
66
  rel="sponsored noopener noreferrer"
@@ -71,22 +71,22 @@
71
71
  </a>
72
72
  </div>
73
73
  </div>
74
- `}static renderAds(e,t=v.renderActionCard.bind(v)){return e.map(t).join(`
75
- `)}}const D="ad_session_id";function H(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}function F(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function V(r){if(r)return r;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,w=()=>G.getSessionId();class k{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${V(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${V(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let s=null;for(let n=0;n<=this.retryAttempts;n++){const o=this.debug||F();try{o&&(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=H();l&&(d["X-API-Key"]=l,o&&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 o&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}catch(a){s=a,o&&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 B=new k,I=(r,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackImpression(r):B.trackImpression(r),M=(r,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackClick(r):B.trackClick(r);function j(r,e){return`vt_${r}_${e}`}async function z(r){return Promise.all(r.map(e=>I(e)))}async function W(r){return Promise.all(r.map(e=>M(e)))}function Q(r){const e=new k(r);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function J(r={}){const e=new U(r);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const _=class _{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 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",o=>{o.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",o=>{o.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=v.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(o=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,o,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,o=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(o))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(o)||(a=!0,this.trackedImpressionKeys.add(o),d())},d=()=>{s?I({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:w(),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(A=>{l=A.isIntersecting&&A.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=b(e.adapted.body||"");return t==="inline"?`
74
+ `}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
75
+ `)}}const H="ad_session_id";function G(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}function D(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function T(r){if(r)return r;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||H,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 $,k=()=>F.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(`${T(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${T(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,n){let s=null;for(let i=0;i<=this.retryAttempts;i++){const o=this.debug||D();try{o&&(console.log(`[Analytics] Request URL (${n}):`,e),console.log(`[Analytics] Sending ${n} event (attempt ${i+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),u={"Content-Type":"application/json"},l=G();l&&(u["X-API-Key"]=l,o&&console.log(`[Analytics] Adding X-API-Key header for ${n} event`));const d=await fetch(e,{method:"POST",headers:u,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!d.ok)throw new Error(`HTTP ${d.status}: ${d.statusText}`);const f=await d.json();return o&&console.log(`[Analytics] ${n} event tracked successfully:`,f),f}catch(a){s=a,o&&console.warn(`[Analytics] ${n} tracking failed (attempt ${i+1}):`,a),i<this.retryAttempts&&await this.delay(this.retryDelay*(i+1))}}return console.error(`[Analytics] ${n} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new A,x=(r,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackImpression(r):O.trackImpression(r),_=(r,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackClick(r):O.trackClick(r);function V(r,e){return`vt_${r}_${e}`}async function j(r){return Promise.all(r.map(e=>x(e)))}async function z(r){return Promise.all(r.map(e=>_(e)))}function J(r){const e=new A(r);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function W(r={}){const e=new $(r);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,n={}){t.innerHTML=w.renderActionCard(e,n);const s=t.querySelector(".ax-ad-card-link");return s&&s.addEventListener("click",i=>{i.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderSuffixAd(e,t,n={}){const s=n.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const i=t.querySelector(".ax-ad-suffix-link");return i&&i.addEventListener("click",o=>{o.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderSponsoredSource(e,t,n={}){const s=n.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const i=t.querySelector(".ax-ad-source-link");return i&&i.addEventListener("click",o=>{o.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderLeadGenAd(e,t,n={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",i=>{i.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderAds(e,t,n=this.renderActionCard.bind(this),s){t.innerHTML="";const i=document.createElement("div");return i.className="ax-ads-container",e.forEach(o=>{const a=document.createElement("div");a.className="ax-ad-wrapper",n.call(this,o,a,s),i.appendChild(a)}),t.appendChild(i),t}static handleClick(e,t){const{onClick:n}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}n&&n(e),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,n){const{analytics:s,onImpression:i}=n,o=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(o))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(o)||(a=!0,this.trackedImpressionKeys.add(o),u())},u=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:k(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{i&&i(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):i&&i(e)};if(typeof IntersectionObserver<"u"){let l=!1,d=null;const f=new IntersectionObserver(h=>{h.forEach(v=>{l=v.isIntersecting&&v.intersectionRatio>=.5,l?d||(d=setTimeout(()=>{d=null,l&&(c(),f.disconnect())},1e3)):d&&(clearTimeout(d),d=null)})},{threshold:.5}),g=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(g)}else c()}static generateSuffixAdHTML(e,t){const n=e.adapted.title||"",s=I(e.adapted.body||"");return t==="inline"?`
76
76
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
77
- ${i}
77
+ ${n}
78
78
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
79
79
  <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" />
80
80
  </svg>
81
81
  </span>
82
82
  `:t==="minimal"?`
83
83
  <span class="ax-ad-suffix-link ax-ad-variant-minimal" data-ad-id="${e.original.id}">
84
- ${i}
84
+ ${n}
85
85
  </span>
86
86
  `:`
87
87
  <div class="ax-ad-suffix ax-ad-variant-block" data-ad-id="${e.original.id}">
88
88
  <div class="ax-ad-suffix-content">
89
- <p class="ax-ad-suffix-title">${i}</p>
89
+ <p class="ax-ad-suffix-title">${n}</p>
90
90
  <div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
91
91
  <span class="ax-ad-sponsored-badge">Sponsored</span>
92
92
  <svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24">
@@ -96,11 +96,11 @@
96
96
  </div>
97
97
  ${s?`<p class="ax-ad-suffix-body">${s}</p>`:""}
98
98
  </div>
99
- `}static generateSponsoredSourceHTML(e,t){var o;const i=e.adapted.title||"",s=b(e.adapted.body||""),n=((o=e.adapted.image)==null?void 0:o.url)||"";return t==="list-item"?`
99
+ `}static generateSponsoredSourceHTML(e,t){var o;const n=e.adapted.title||"",s=I(e.adapted.body||""),i=((o=e.adapted.image)==null?void 0:o.url)||"";return t==="list-item"?`
100
100
  <div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
101
- ${n?`<img src="${n}" alt="${i}" loading="lazy" />`:""}
101
+ ${i?`<img src="${i}" alt="${n}" loading="lazy" />`:""}
102
102
  <div class="ax-ad-source-info">
103
- <span class="ax-ad-source-name">${i}</span>
103
+ <span class="ax-ad-source-name">${n}</span>
104
104
  <span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
105
105
  </div>
106
106
  <svg width="12" height="12" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
@@ -110,17 +110,17 @@
110
110
  `:t==="minimal"?`
111
111
  <div data-ad-id="${e.original.id}" style="display:inline-flex">
112
112
  <a class="ax-ad-source-link ax-ad-variant-minimal">
113
- ${n?`<img src="${n}" alt="${i}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
114
- <span>${i}</span>
113
+ ${i?`<img src="${i}" alt="${n}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
114
+ <span>${n}</span>
115
115
  <span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
116
116
  </a>
117
117
  </div>
118
118
  `:`
119
119
  <div class="ax-ad-source ax-ad-variant-card" data-ad-id="${e.original.id}">
120
- ${n?`<img src="${n}" alt="${i}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
120
+ ${i?`<img src="${i}" alt="${n}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
121
121
  <div class="ax-ad-source-info">
122
122
  <div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
123
- <span class="ax-ad-source-name">${i}</span>
123
+ <span class="ax-ad-source-name">${n}</span>
124
124
  <span class="ax-ad-sponsored-badge">Sponsored</span>
125
125
  </div>
126
126
  ${s?`<p class="ax-ad-source-desc">${s}</p>`:""}
@@ -129,4 +129,4 @@
129
129
  <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" />
130
130
  </svg>
131
131
  </div>
132
- `}};_.trackedImpressionKeys=new Set;let 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 o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[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 y(r){return T().collect(r)}function X(){return y().device}function q(){return y().user}function Z(){return y().app}function Y(){return y().geo}function L(){return q().id}function ee(){T().clearCache()}function te(r){const e=y(r);return JSON.stringify(e,null,2)}function ie(){var t;const r=y();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function E(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function se(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function R(r,e={}){const t=e.apiBaseUrl||"/api/v1",s=T().collect(),n={...r,clientInfo:s},o={"Content-Type":"application/json"};e.apiKey&&(o["X-API-Key"]=e.apiKey),E()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",r),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:o,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(E()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const ne={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function P(r={}){const e={variant:r.variant??C.variant,count:r.count??C.count,preferences:{...C.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ne,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 N,this.slots_config=P(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 o=await R({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??w()}:{userId:this.currentUserId,sessionId:w()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=o.requestId,this.adsAnalyticsMap.clear(),o.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:o.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return o.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)}),o}catch(o){const a=o;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 L()}catch{return}}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,o=s?e:t,a=(s?t:i)||{};if(!o)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(A=>A.slotId===n),p=(h==null?void 0:h.format)||"action_card";o.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?m.renderSuffixAd(l,o,f):p==="source"?m.renderSponsoredSource(l,o,f):p==="lead_gen"?m.renderLeadGenAd(l,o,f):m.renderActionCard(l,o,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=P(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=w(),o=await M({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 o&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}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=w(),n=await I({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 O(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}class K{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 o={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,o),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 o=this.metrics.get(e);if(!o||!this.isTracking.get(e))return;const a=o.isViewable,c=Math.round(t.intersectionRatio*100);o.visiblePercentage=c,o.maxVisiblePercentage=Math.max(o.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!o.enteredViewportAt&&(o.enteredViewportAt=d,o.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:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&o.enteredViewportAt){const u=d-o.enteredViewportAt;o.totalVisibleTimeMs+=u,o.enteredViewportAt=null,o.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${o.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:o.isViewable,timestamp:d})}o.isViewable!==a&&o.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,o)}startMonitoring(e,t,i,s){const o=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(o);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`,o)}queueEvent(e,t){var n,o;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=((o=this.config.batchConfig)==null?void 0:o.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=O();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 o=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:o}),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=O();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let x=null;function re(r,e){return x||(x=new K(r,e),x.setupBeforeUnload()),x}const oe="0.1.0";exports.AdManager=$;exports.AnalyticsSender=k;exports.ClientInfoCollector=S;exports.DOMRenderer=m;exports.HTMLRenderer=v;exports.SDK_VERSION=oe;exports.SessionManager=U;exports.ViewabilityTracker=K;exports.adaptAdToKoahAd=se;exports.clearClientInfoCache=ee;exports.createAnalytics=Q;exports.createSession=J;exports.fetchAds=R;exports.generateViewToken=j;exports.getAppInfo=Z;exports.getClientInfo=y;exports.getClientInfoCollector=T;exports.getClientInfoJSON=te;exports.getClientInfoSummary=ie;exports.getDeviceInfo=X;exports.getGeoInfo=Y;exports.getSessionId=w;exports.getUserId=L;exports.getUserInfo=q;exports.getViewabilityTracker=re;exports.trackAdClick=M;exports.trackAdImpression=I;exports.trackClicksBatch=W;exports.trackImpressionsBatch=z;
132
+ `}};M.trackedImpressionKeys=new Set;let m=M;class b{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(),n=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:n,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,i;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 n=(i=document.querySelector('meta[name="application-name"]'))==null?void 0:i.getAttribute("content");return n||"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 n=document.querySelector(t);if(n){if(t.includes("manifest"))return"1.0.0";const s=n.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var n,s;if(typeof document>"u")return"unknown";const e=(n=document.querySelector('meta[name="publisher-id"]'))==null?void 0:n.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 b),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const n=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:n.connectiontype??0,bandwidth:n.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),n=this.detectOSVersion(e);return{ua:e,os:t,osv:n,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 n=e.match(/Android (\d+)\.(\d+)/);if(n)return`${n[1]}.${n[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const i=e.match(/Mac OS X (\d+[._]\d+)/);return i?i[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 n=e.match(/Samsung-.*(SM-\w+)/);if(n)return n[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 i=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(i)return i[1]+" "+i[2];if(/OnePlus/.test(e)){const o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[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 n={"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&&n[t])return n[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=()=>b.getInstance();function y(r){return S().collect(r)}function X(){return y().device}function L(){return y().user}function Z(){return y().app}function Y(){return y().geo}function P(){return L().id}function Q(){S().clearCache()}function ee(r){const e=y(r);return JSON.stringify(e,null,2)}function te(){var t;const r=y();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function q(){if(typeof crypto>"u"||!crypto.subtle)throw new Error("Web Crypto API not supported")}async function K(r,e){q();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),n=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["encrypt"]),s=crypto.getRandomValues(new Uint8Array(12)),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:s},n,new TextEncoder().encode(r)),o=new Uint8Array(s.length+i.byteLength);return o.set(s),o.set(new Uint8Array(i),s.length),btoa(String.fromCharCode(...o))}async function B(r,e){q();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),n=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]),s=Uint8Array.from(atob(r),c=>c.charCodeAt(0)),i=s.slice(0,12),o=s.slice(12),a=await crypto.subtle.decrypt({name:"AES-GCM",iv:i},n,o);return new TextDecoder().decode(a)}function ne(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function se(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function R(r,e={}){const t=e.apiBaseUrl||"/api/v1",n=ne(),i=S().collect(),o={...r,clientInfo:i},a={"Content-Type":"application/json"};e.apiKey&&(a["X-API-Key"]=e.apiKey);let c;if(e.apiKey&&!n)try{c={encryptedPayload:await K(JSON.stringify(o),e.apiKey)}}catch(f){n&&console.warn("[fetchAds] Encryption failed, using plaintext:",f),c=o}else c=o;n&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request body:",c));const u=await fetch(`${t}/ads/request`,{method:"POST",headers:a,body:JSON.stringify(c)});if(!u.ok)throw new Error(`Ad request failed: ${u.status} ${u.statusText}`);const l=await u.json();if(!l.success)throw new Error(l.error||"Ad request failed");let d=l.data;if(l.data.encryptedPayload&&e.apiKey){const f=await B(l.data.encryptedPayload,e.apiKey);d=JSON.parse(f)}return n&&console.log("[fetchAds] Response:",d),d}const ie={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function E(r={}){const e={variant:r.variant??C.variant,count:r.count??C.count,preferences:{...C.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ie,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 N,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 n=this.buildRequestKey(t),s=p.inFlightRequests.get(n);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 i=(async()=>{try{const o=await R({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??k()}:{userId:this.currentUserId,sessionId:k()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=o.requestId,this.adsAnalyticsMap.clear(),o.slots.forEach(c=>{c.ads.forEach((u,l)=>{const d=u.original.id;this.adsAnalyticsMap.set(d,{requestId:o.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return o.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)}),o}catch(o){const a=o;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,p.inFlightRequests.delete(n)}})();return p.inFlightRequests.set(n,i),i}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 P()}catch{return}}render(e,t,n={}){const s=typeof e!="string",i=s?p.DEFAULT_SLOT_ID:e,o=s?e:t,a=(s?t:n)||{};if(!o)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[i];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",i),null;const u=a.adIndex??0,l=c.ads[u];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,i),null;const d=this.adsAnalyticsMap.get(l.original.id),f=this.slots_config.find(v=>v.slotId===i),g=(f==null?void 0:f.format)||"action_card";o.innerHTML="";const h={analytics:d,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return g==="suffix"?m.renderSuffixAd(l,o,h):g==="source"?m.renderSponsoredSource(l,o,h):g==="lead_gen"?m.renderLeadGenAd(l,o,h):m.renderActionCard(l,o,h)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var n;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((n=t.ads)==null?void 0:n.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(n=>{const s=this.adsAnalyticsMap.get(n);s&&t.set(n,s)}),t}async trackAdClick(e,t,n){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 i=k(),o=await _({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:i,slotId:s.slotId,adTitle:n==null?void 0:n.title,format:n==null?void 0:n.format,source:n==null?void 0:n.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return o&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}async trackAdImpressionAPI(e,t){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=k(),i=await x({requestId:this.currentRequestId,adId:e,slotId:n.slotId,position:n.position,totalAds:n.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:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return i&&(this.eventBus.emit("adImpression",e,n.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",i.eventId)),i}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let U=p;const re="0.1.0";exports.AdManager=U;exports.AnalyticsSender=A;exports.ClientInfoCollector=b;exports.DOMRenderer=m;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=$;exports.adaptAdToKoahAd=se;exports.clearClientInfoCache=Q;exports.createAnalytics=J;exports.createSession=W;exports.decryptAES=B;exports.encryptAES=K;exports.fetchAds=R;exports.generateViewToken=V;exports.getAppInfo=Z;exports.getClientInfo=y;exports.getClientInfoCollector=S;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=X;exports.getGeoInfo=Y;exports.getSessionId=k;exports.getUserId=P;exports.getUserInfo=L;exports.trackAdClick=_;exports.trackAdImpression=x;exports.trackClicksBatch=z;exports.trackImpressionsBatch=j;
package/dist/index.d.ts CHANGED
@@ -680,6 +680,8 @@ export declare interface DecisionResult {
680
680
  routing: RoutingInfo;
681
681
  }
682
682
 
683
+ export declare function decryptAES(base64: string, apiKey: string): Promise<string>;
684
+
683
685
  /**
684
686
  * Device Information (auto-collected by SDK)
685
687
  */
@@ -766,6 +768,11 @@ export declare class DOMRenderer {
766
768
  private static generateSponsoredSourceHTML;
767
769
  }
768
770
 
771
+ /**
772
+ * AES-GCM encryption/decryption using Web Crypto API
773
+ */
774
+ export declare function encryptAES(text: string, apiKey: string): Promise<string>;
775
+
769
776
  /**
770
777
  * Enhanced Content Props
771
778
  * Props for the EnhancedContent component
@@ -1018,11 +1025,6 @@ export declare function getUserId(): string;
1018
1025
  */
1019
1026
  export declare function getUserInfo(): UserInfo;
1020
1027
 
1021
- /**
1022
- * Get or create the viewability tracker singleton
1023
- */
1024
- export declare function getViewabilityTracker(config?: ViewabilityConfig, baseUrl?: string): ViewabilityTracker;
1025
-
1026
1028
  /**
1027
1029
  * Global Suggestions
1028
1030
  */
@@ -1451,139 +1453,4 @@ export declare interface UserProfile {
1451
1453
  };
1452
1454
  }
1453
1455
 
1454
- export declare interface ViewabilityConfig {
1455
- /** Minimum visible percentage to consider "in view" (default: 50) */
1456
- minVisiblePercentage?: number;
1457
- /** Minimum duration in ms to consider "viewable" (default: 1000) */
1458
- minViewableDuration?: number;
1459
- /** Maximum tracking duration in ms (default: 60000 = 1 minute) */
1460
- maxTrackingDuration?: number;
1461
- /** Batch events before sending (default: 5 events or 10 seconds) */
1462
- batchConfig?: {
1463
- maxBatchSize?: number;
1464
- maxBatchWaitMs?: number;
1465
- };
1466
- /** Enable debug logging (default: false) */
1467
- debug?: boolean;
1468
- }
1469
-
1470
- export declare interface ViewabilityEventData {
1471
- adId: string;
1472
- sessionId: string;
1473
- requestId?: string;
1474
- viewToken?: string;
1475
- eventType: ViewabilityEventType;
1476
- visiblePercentage: number;
1477
- maxVisiblePercentage: number;
1478
- totalVisibleTimeMs: number;
1479
- isViewable: boolean;
1480
- viewportWidth?: number;
1481
- viewportHeight?: number;
1482
- elementWidth?: number;
1483
- elementHeight?: number;
1484
- positionX?: number;
1485
- positionY?: number;
1486
- timestamp: number;
1487
- }
1488
-
1489
- /**
1490
- * Viewability Tracking Module (IAB/MRC Compliant)
1491
- *
1492
- * IAB/MRC Standard:
1493
- * - Viewable: >50% of ad pixels in view for >1 second
1494
- * - Uses IntersectionObserver API for efficient tracking
1495
- *
1496
- * Event-driven approach: Only sends data on state changes, not periodic sampling
1497
- */
1498
- export declare type ViewabilityEventType = 'enter_viewport' | 'become_viewable' | 'update_viewability' | 'exit_viewport' | 'end_tracking';
1499
-
1500
- export declare interface ViewabilityMetrics {
1501
- /** Current visible percentage (0-100) */
1502
- visiblePercentage: number;
1503
- /** Maximum visible percentage observed */
1504
- maxVisiblePercentage: number;
1505
- /** Total time visible in ms */
1506
- totalVisibleTimeMs: number;
1507
- /** Current continuous visible time in ms */
1508
- currentVisibleTimeMs: number;
1509
- /** Whether meets IAB/MRC viewable criteria */
1510
- isViewable: boolean;
1511
- /** Time when became viewable (null if not viewable yet) */
1512
- viewableAt: number | null;
1513
- /** Time when entered viewport (null if never entered) */
1514
- enteredViewportAt: number | null;
1515
- /** Total number of times entered viewport */
1516
- enterCount: number;
1517
- }
1518
-
1519
- /**
1520
- * ViewabilityTracker class for tracking ad viewability
1521
- * Event-driven: Only sends data on state changes
1522
- */
1523
- export declare class ViewabilityTracker {
1524
- private observers;
1525
- private metrics;
1526
- private timers;
1527
- private batchTimers;
1528
- private eventQueue;
1529
- private config;
1530
- private baseUrl;
1531
- private isTracking;
1532
- constructor(config?: ViewabilityConfig, baseUrl?: string);
1533
- /**
1534
- * Start tracking viewability for an ad element
1535
- */
1536
- startTracking(adId: string, element: HTMLElement, sessionId: string, requestId?: string, viewToken?: string): void;
1537
- /**
1538
- * Stop tracking an ad element
1539
- */
1540
- stopTracking(adId: string): void;
1541
- /**
1542
- * Get current metrics for an ad
1543
- */
1544
- getMetrics(adId: string): ViewabilityMetrics | undefined;
1545
- /**
1546
- * Handle IntersectionObserver callback
1547
- * Event-driven: Only process when state actually changes
1548
- */
1549
- private handleIntersection;
1550
- /**
1551
- * Start monitoring loop for tracking duration
1552
- * Runs every 100ms to track continuous visible time
1553
- */
1554
- private startMonitoring;
1555
- /**
1556
- * Queue an event to be sent (batched)
1557
- */
1558
- private queueEvent;
1559
- /**
1560
- * Flush the event queue and send to server
1561
- */
1562
- private flushQueue;
1563
- /**
1564
- * End tracking and send final metrics
1565
- */
1566
- private endTracking;
1567
- /**
1568
- * Cleanup resources for an ad
1569
- */
1570
- private cleanup;
1571
- /**
1572
- * Create thresholds for IntersectionObserver
1573
- */
1574
- private createThresholds;
1575
- /**
1576
- * Stop all tracking
1577
- */
1578
- stopAll(): void;
1579
- /**
1580
- * Debug logging
1581
- */
1582
- private log;
1583
- /**
1584
- * Send any pending events before page unload
1585
- */
1586
- setupBeforeUnload(): void;
1587
- }
1588
-
1589
1456
  export { }