@action-x/ad-sdk 0.1.10 → 0.1.12

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,40 +1,42 @@
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
- src="${n.image.url}"
3
- alt="${n.title}"
4
- class="ax-ad-image"
5
- loading="lazy"
6
- />`:"",d=(n.brand||"").trim(),l=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${n.brand}</span>`:"",u=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class D{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}}function b(o){return o.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:n=!0}=t,s=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(f=s.image)!=null&&f.url?`<div class="ax-ad-image-wrap">
2
+ <img
3
+ src="${s.image.url}"
4
+ alt="${s.title}"
5
+ class="ax-ad-image"
6
+ loading="lazy"
7
+ />
8
+ </div>`:"",d=(s.brand||"").trim(),l=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${s.brand}</span>`:"",u=`
7
9
  ${c}
8
10
  <div class="ax-ad-content">
9
11
  ${l}
10
- <h3 class="ax-ad-title">${n.title}</h3>
11
- ${n.body?`<p class="ax-ad-body">${n.body}</p>`:""}
12
+ <h3 class="ax-ad-title">${s.title}</h3>
13
+ ${s.body?`<p class="ax-ad-body">${b(s.body)}</p>`:""}
12
14
  </div>
13
- `,h=s?`<a
15
+ `,h=n?`<a
14
16
  href="${r}"
15
17
  class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
16
18
  target="_blank"
17
19
  rel="sponsored noopener noreferrer"
18
20
  data-ad-id="${e.original.id}"
19
21
  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`
22
+ >`:"",p=n?"</a>":"";return h+u+p}static renderSuffixAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
21
23
  <div class="ax-ad-suffix" data-ad-id="${e.original.id}">
22
24
  <div class="ax-ad-suffix-content">
23
25
  ${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
24
- ${t.body?`<p class="ax-ad-suffix-body">${t.body}</p>`:""}
26
+ ${t.body?`<p class="ax-ad-suffix-body">${b(t.body)}</p>`:""}
25
27
  <a
26
28
  href="${i}"
27
29
  class="ax-ad-suffix-link"
28
30
  target="_blank"
29
31
  rel="sponsored noopener noreferrer"
30
32
  data-ad-id="${e.original.id}"
31
- data-impression-url="${s}"
33
+ data-impression-url="${n}"
32
34
  >
33
35
  ${this.getCtaText(e)}
34
36
  </a>
35
37
  </div>
36
38
  </div>
37
- `}static renderSponsoredSource(e){var n;const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
39
+ `}static renderSponsoredSource(e){var s;const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
38
40
  <div class="ax-ad-source" data-ad-id="${e.original.id}">
39
41
  <a
40
42
  href="${i}"
@@ -42,35 +44,35 @@
42
44
  target="_blank"
43
45
  rel="sponsored noopener noreferrer"
44
46
  data-ad-id="${e.original.id}"
45
- data-impression-url="${s}"
47
+ data-impression-url="${n}"
46
48
  >
47
- ${(n=t.image)!=null&&n.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
49
+ ${(s=t.image)!=null&&s.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
48
50
  <div class="ax-ad-source-info">
49
51
  <span class="ax-ad-source-name">${t.title}</span>
50
- ${t.body?`<span class="ax-ad-source-desc">${t.body}</span>`:""}
52
+ ${t.body?`<span class="ax-ad-source-desc">${b(t.body)}</span>`:""}
51
53
  </div>
52
54
  <span class="ax-ad-source-badge">Sponsored</span>
53
55
  </a>
54
56
  </div>
55
- `}static renderLeadGenAd(e){const t=e.adapted,i=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
57
+ `}static renderLeadGenAd(e){const t=e.adapted,i=this.getClickUrl(e),n=this.getImpressionUrl(e);return`
56
58
  <div class="ax-ad-leadgen" data-ad-id="${e.original.id}">
57
59
  <div class="ax-ad-leadgen-content">
58
60
  ${t.title?`<h3 class="ax-ad-leadgen-title">${t.title}</h3>`:""}
59
- ${t.body?`<p class="ax-ad-leadgen-body">${t.body}</p>`:""}
61
+ ${t.body?`<p class="ax-ad-leadgen-body">${b(t.body)}</p>`:""}
60
62
  <a
61
63
  href="${i}"
62
64
  class="ax-ad-leadgen-cta"
63
65
  target="_blank"
64
66
  rel="sponsored noopener noreferrer"
65
67
  data-ad-id="${e.original.id}"
66
- data-impression-url="${s}"
68
+ data-impression-url="${n}"
67
69
  >
68
70
  ${this.getCtaText(e)}
69
71
  </a>
70
72
  </div>
71
73
  </div>
72
- `}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
73
- `)}}const N="ad_session_id";function D(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function H(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function _(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class ${constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||N,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const F=new $,b=()=>F.getSessionId();class v{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let s=null;for(let n=0;n<=this.retryAttempts;n++){const r=this.debug||H();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${n+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),d={"Content-Type":"application/json"},l=D();l&&(d["X-API-Key"]=l,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const u=await fetch(e,{method:"POST",headers:d,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!u.ok)throw new Error(`HTTP ${u.status}: ${u.statusText}`);const h=await u.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}catch(a){s=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${n+1}):`,a),n<this.retryAttempts&&await this.delay(this.retryDelay*(n+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new v,x=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackImpression(o):O.trackImpression(o),U=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackClick(o):O.trackClick(o);function G(o,e){return`vt_${o}_${e}`}async function j(o){return Promise.all(o.map(e=>x(e)))}async function z(o){return Promise.all(o.map(e=>U(e)))}function W(o){const e=new v(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function Q(o={}){const e=new $(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const M=class M{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=w.renderActionCard(e,i);const s=t.querySelector(".ax-ad-card-link");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const s=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const n=t.querySelector(".ax-ad-suffix-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const s=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const n=t.querySelector(".ax-ad-source-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),s){t.innerHTML="";const n=document.createElement("div");return n.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,s),n.appendChild(a)}),t.appendChild(n),t}static handleClick(e,t){const{onClick:i}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}i&&i(e),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:s,onImpression:n}=i,r=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),d())},d=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:b(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{n&&n(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):n&&n(e)};if(typeof IntersectionObserver<"u"){let l=!1,u=null;const h=new IntersectionObserver(f=>{f.forEach(k=>{l=k.isIntersecting&&k.intersectionRatio>=.5,l?u||(u=setTimeout(()=>{u=null,l&&(c(),h.disconnect())},1e3)):u&&(clearTimeout(u),u=null)})},{threshold:.5}),p=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;h.observe(p)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",s=e.adapted.body||"";return t==="inline"?`
74
+ `}static renderAds(e,t=v.renderActionCard.bind(v)){return e.map(t).join(`
75
+ `)}}const G="ad_session_id";function F(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function j(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function V(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||G,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 z=new U,w=()=>z.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 n=null;for(let s=0;s<=this.retryAttempts;s++){const r=this.debug||j();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),d={"Content-Type":"application/json"},l=F();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){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 O=new k,S=(o,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackImpression(o):O.trackImpression(o),M=(o,e)=>e!=null&&e.baseUrl?new k({baseUrl:e.baseUrl}).trackClick(o):O.trackClick(o);function W(o,e){return`vt_${o}_${e}`}async function Q(o){return Promise.all(o.map(e=>S(e)))}async function J(o){return Promise.all(o.map(e=>M(e)))}function X(o){const e=new k(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function Z(o={}){const e=new U(o);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 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=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{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),d())},d=()=>{n?S({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:w(),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(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):s&&s(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||"",n=b(e.adapted.body||"");return t==="inline"?`
74
76
  <span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
75
77
  ${i}
76
78
  <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
@@ -92,11 +94,11 @@
92
94
  </svg>
93
95
  </div>
94
96
  </div>
95
- ${s?`<p class="ax-ad-suffix-body">${s}</p>`:""}
97
+ ${n?`<p class="ax-ad-suffix-body">${n}</p>`:""}
96
98
  </div>
97
- `}static generateSponsoredSourceHTML(e,t){var r;const i=e.adapted.title||"",s=e.adapted.body||"",n=((r=e.adapted.image)==null?void 0:r.url)||"";return t==="list-item"?`
99
+ `}static generateSponsoredSourceHTML(e,t){var r;const i=e.adapted.title||"",n=b(e.adapted.body||""),s=((r=e.adapted.image)==null?void 0:r.url)||"";return t==="list-item"?`
98
100
  <div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
99
- ${n?`<img src="${n}" alt="${i}" loading="lazy" />`:""}
101
+ ${s?`<img src="${s}" alt="${i}" loading="lazy" />`:""}
100
102
  <div class="ax-ad-source-info">
101
103
  <span class="ax-ad-source-name">${i}</span>
102
104
  <span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
@@ -108,23 +110,23 @@
108
110
  `:t==="minimal"?`
109
111
  <div data-ad-id="${e.original.id}" style="display:inline-flex">
110
112
  <a class="ax-ad-source-link ax-ad-variant-minimal">
111
- ${n?`<img src="${n}" alt="${i}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
113
+ ${s?`<img src="${s}" alt="${i}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
112
114
  <span>${i}</span>
113
115
  <span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
114
116
  </a>
115
117
  </div>
116
118
  `:`
117
119
  <div class="ax-ad-source ax-ad-variant-card" data-ad-id="${e.original.id}">
118
- ${n?`<img src="${n}" alt="${i}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
120
+ ${s?`<img src="${s}" alt="${i}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
119
121
  <div class="ax-ad-source-info">
120
122
  <div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
121
123
  <span class="ax-ad-source-name">${i}</span>
122
124
  <span class="ax-ad-sponsored-badge">Sponsored</span>
123
125
  </div>
124
- ${s?`<p class="ax-ad-source-desc">${s}</p>`:""}
126
+ ${n?`<p class="ax-ad-source-desc">${n}</p>`:""}
125
127
  </div>
126
128
  <svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
127
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" />
128
130
  </svg>
129
131
  </div>
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;
132
+ `}};_.trackedImpressionKeys=new Set;let 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(),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 T=()=>I.getInstance();function y(o){return T().collect(o)}function Y(){return y().device}function B(){return y().user}function ee(){return y().app}function te(){return y().geo}function q(){return B().id}function ie(){T().clearCache()}function ne(o){const e=y(o);return JSON.stringify(e,null,2)}function se(){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 L(){if(typeof crypto>"u"||!crypto.subtle)throw new Error("Web Crypto API not supported")}async function K(o,e){L();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),i=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["encrypt"]),n=crypto.getRandomValues(new Uint8Array(12)),s=await crypto.subtle.encrypt({name:"AES-GCM",iv:n},i,new TextEncoder().encode(o)),r=new Uint8Array(n.length+s.byteLength);return r.set(n),r.set(new Uint8Array(s),n.length),btoa(String.fromCharCode(...r))}async function R(o,e){L();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),i=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]),n=Uint8Array.from(atob(o),c=>c.charCodeAt(0)),s=n.slice(0,12),r=n.slice(12),a=await crypto.subtle.decrypt({name:"AES-GCM",iv:s},i,r);return new TextDecoder().decode(a)}function re(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function oe(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 N(o,e={}){const t=e.apiBaseUrl||"/api/v1",i=re(),s=T().collect(),r={...o,clientInfo:s},a={"Content-Type":"application/json"};e.apiKey&&(a["X-API-Key"]=e.apiKey);let c;if(e.apiKey&&!i)try{c={encryptedPayload:await K(JSON.stringify(r),e.apiKey)}}catch(h){i&&console.warn("[fetchAds] Encryption failed, using plaintext:",h),c=r}else c=r;i&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request body:",c));const d=await fetch(`${t}/ads/request`,{method:"POST",headers:a,body:JSON.stringify(c)});if(!d.ok)throw new Error(`Ad request failed: ${d.status} ${d.statusText}`);const l=await d.json();if(!l.success)throw new Error(l.error||"Ad request failed");let u=l.data;if(l.data.encryptedPayload&&e.apiKey){const h=await R(l.data.encryptedPayload,e.apiKey);u=JSON.parse(h)}return i&&console.log("[fetchAds] Response:",u),u}const ae={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:ae,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 D,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),n=g.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 N({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=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,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 q()}catch{return}}render(e,t,i={}){const n=typeof e!="string",s=n?g.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 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:`,s),null;const u=this.adsAnalyticsMap.get(l.original.id),h=this.slots_config.find(A=>A.slotId===s),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 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=w(),r=await M({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=w(),s=await S({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")}};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 H{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(d=>this.handleIntersection(e,d[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 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:n,viewToken:s,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:n,viewToken:s,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,n){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: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,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 n={"Content-Type":"application/json"},s=P();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=P();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let x=null;function ce(o,e){return x||(x=new H(o,e),x.setupBeforeUnload()),x}const le="0.1.0";exports.AdManager=$;exports.AnalyticsSender=k;exports.ClientInfoCollector=I;exports.DOMRenderer=m;exports.HTMLRenderer=v;exports.SDK_VERSION=le;exports.SessionManager=U;exports.ViewabilityTracker=H;exports.adaptAdToKoahAd=oe;exports.clearClientInfoCache=ie;exports.createAnalytics=X;exports.createSession=Z;exports.decryptAES=R;exports.encryptAES=K;exports.fetchAds=N;exports.generateViewToken=W;exports.getAppInfo=ee;exports.getClientInfo=y;exports.getClientInfoCollector=T;exports.getClientInfoJSON=ne;exports.getClientInfoSummary=se;exports.getDeviceInfo=Y;exports.getGeoInfo=te;exports.getSessionId=w;exports.getUserId=q;exports.getUserInfo=B;exports.getViewabilityTracker=ce;exports.trackAdClick=M;exports.trackAdImpression=S;exports.trackClicksBatch=J;exports.trackImpressionsBatch=Q;
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