@action-x/ad-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +678 -0
- package/dist/index.cjs +139 -0
- package/dist/index.d.ts +1552 -0
- package/dist/index.js +1478 -0
- package/dist/index.umd.js +139 -0
- package/dist/style.css +1 -0
- package/package.json +30 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class E{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){console.error(`[EventEmitter] Error in event handler for "${e}":`,n)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class f{static renderActionCard(e,t={}){var x;const{variant:i="horizontal",includeWrapper:n=!0}=t,s=e.adapted,r=e.tracking,a=n?`<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">`:"",c=(x=s.image)!=null&&x.url?`<img
|
|
2
|
+
src="${s.image.url}"
|
|
3
|
+
alt="${s.title}"
|
|
4
|
+
class="ax-ad-image"
|
|
5
|
+
loading="lazy"
|
|
6
|
+
/>`:"",l=s.price?`<span class="ax-ad-price">${s.price.display||s.price.value}</span>`:"",d=s.rating?`<div class="ax-ad-rating" aria-label="Rating: ${s.rating}">
|
|
7
|
+
${"★".repeat(Math.floor(s.rating))}${"☆".repeat(5-Math.floor(s.rating))}
|
|
8
|
+
</div>`:"",u=s.brand?`<span class="ax-ad-brand">${s.brand}</span>`:"",C=`
|
|
9
|
+
${c}
|
|
10
|
+
<div class="ax-ad-content">
|
|
11
|
+
${u}
|
|
12
|
+
<h3 class="ax-ad-title">${s.title}</h3>
|
|
13
|
+
${s.body?`<p class="ax-ad-body">${s.body}</p>`:""}
|
|
14
|
+
${d}
|
|
15
|
+
<div class="ax-ad-footer">
|
|
16
|
+
${l}
|
|
17
|
+
<a
|
|
18
|
+
href="${r.clickUrl}"
|
|
19
|
+
class="ax-ad-cta"
|
|
20
|
+
target="_blank"
|
|
21
|
+
rel="sponsored noopener noreferrer"
|
|
22
|
+
data-ad-id="${e.original.id}"
|
|
23
|
+
data-impression-url="${r.impressionUrl}"
|
|
24
|
+
>
|
|
25
|
+
${s.ctaText||"Learn More"}
|
|
26
|
+
</a>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
`,V=n?"</div>":"";return a+C+V}static renderSuffixAd(e){const t=e.adapted,i=e.tracking;return`
|
|
30
|
+
<div class="ax-ad-suffix" data-ad-id="${e.original.id}">
|
|
31
|
+
<div class="ax-ad-suffix-content">
|
|
32
|
+
${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
|
|
33
|
+
${t.body?`<p class="ax-ad-suffix-body">${t.body}</p>`:""}
|
|
34
|
+
<a
|
|
35
|
+
href="${i.clickUrl}"
|
|
36
|
+
class="ax-ad-suffix-link"
|
|
37
|
+
target="_blank"
|
|
38
|
+
rel="sponsored noopener noreferrer"
|
|
39
|
+
data-ad-id="${e.original.id}"
|
|
40
|
+
data-impression-url="${i.impressionUrl}"
|
|
41
|
+
>
|
|
42
|
+
${t.ctaText||"Learn More"}
|
|
43
|
+
</a>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
`}static renderSponsoredSource(e){var n;const t=e.adapted,i=e.tracking;return`
|
|
47
|
+
<div class="ax-ad-source" data-ad-id="${e.original.id}">
|
|
48
|
+
<a
|
|
49
|
+
href="${i.clickUrl}"
|
|
50
|
+
class="ax-ad-source-link"
|
|
51
|
+
target="_blank"
|
|
52
|
+
rel="sponsored noopener noreferrer"
|
|
53
|
+
data-ad-id="${e.original.id}"
|
|
54
|
+
data-impression-url="${i.impressionUrl}"
|
|
55
|
+
>
|
|
56
|
+
${(n=t.image)!=null&&n.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
|
|
57
|
+
<div class="ax-ad-source-info">
|
|
58
|
+
<span class="ax-ad-source-name">${t.title}</span>
|
|
59
|
+
${t.body?`<span class="ax-ad-source-desc">${t.body}</span>`:""}
|
|
60
|
+
</div>
|
|
61
|
+
<span class="ax-ad-source-badge">Sponsored</span>
|
|
62
|
+
</a>
|
|
63
|
+
</div>
|
|
64
|
+
`}static renderLeadGenAd(e){const t=e.adapted,i=e.tracking;return`
|
|
65
|
+
<div class="ax-ad-leadgen" data-ad-id="${e.original.id}">
|
|
66
|
+
<div class="ax-ad-leadgen-content">
|
|
67
|
+
${t.title?`<h3 class="ax-ad-leadgen-title">${t.title}</h3>`:""}
|
|
68
|
+
${t.body?`<p class="ax-ad-leadgen-body">${t.body}</p>`:""}
|
|
69
|
+
<a
|
|
70
|
+
href="${i.clickUrl}"
|
|
71
|
+
class="ax-ad-leadgen-cta"
|
|
72
|
+
target="_blank"
|
|
73
|
+
rel="sponsored noopener noreferrer"
|
|
74
|
+
data-ad-id="${e.original.id}"
|
|
75
|
+
data-impression-url="${i.impressionUrl}"
|
|
76
|
+
>
|
|
77
|
+
${t.ctaText||"Get Started"}
|
|
78
|
+
</a>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
`}static renderAds(e,t=f.renderActionCard.bind(f)){return e.map(t).join(`
|
|
82
|
+
`)}}const P="/api/v1/ads",U="ad_session_id";function _(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class k{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||U,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 O=new k,p=()=>O.getSessionId();class A{constructor(e={}){this.baseUrl=e.baseUrl||P,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.baseUrl}/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${this.baseUrl}/click`,e,"click")}async sendRequest(e,t,i){let n=null;for(let s=0;s<=this.retryAttempts;s++)try{this.debug&&console.log(`[Analytics] Sending ${i} event (attempt ${s+1}):`,t);const r=new AbortController,a=setTimeout(()=>r.abort(),this.timeout),c={"Content-Type":"application/json"},l=_();l&&(c["X-API-Key"]=l,this.debug&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const d=await fetch(e,{method:"POST",headers:c,body:JSON.stringify(t),keepalive:!0,signal:r.signal});if(clearTimeout(a),!d.ok)throw new Error(`HTTP ${d.status}: ${d.statusText}`);const u=await d.json();return this.debug&&console.log(`[Analytics] ${i} event tracked successfully:`,u),u}catch(r){n=r,this.debug&&console.warn(`[Analytics] ${i} tracking failed (attempt ${s+1}):`,r),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 T=new A,y=o=>T.trackImpression(o),b=o=>T.trackClick(o);function B(o,e){return`vt_${o}_${e}`}async function L(o){return Promise.all(o.map(e=>y(e)))}async function q(o){return Promise.all(o.map(e=>b(e)))}function K(o){const e=new A(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function R(o={}){const e=new k(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}class g{static renderActionCard(e,t,i={}){t.innerHTML=f.renderActionCard(e,i);const n=t.querySelector(".ax-ad-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const n=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,n);const s=t.querySelector(".ax-ad-suffix-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const n=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,n);const s=t.querySelector(".ax-ad-source-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=f.renderLeadGenAd(e);const n=t.querySelector(".ax-ad-leadgen-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),n){t.innerHTML="";const s=document.createElement("div");return s.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,n),s.appendChild(a)}),t.appendChild(s),t}static handleClick(e,t){const{analytics:i,onClick:n}=t;n&&n(e),i&&b({requestId:i.requestId,adId:e.original.id,destinationUrl:e.tracking.clickUrl,slotId:i.slotId,sessionId:p(),adTitle:e.adapted.title,format:e.original.type}).catch(s=>{console.error("[DOMRenderer] Analytics click tracking failed:",s)}),window.open(e.tracking.clickUrl,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:n,onImpression:s}=i,r=()=>{n?y({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:p(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:e.tracking.viewToken}).then(()=>{s&&s(e)}).catch(a=>{console.error("[DOMRenderer] Analytics impression tracking failed:",a)}):s&&s(e)};if(typeof IntersectionObserver<"u"){const a=new IntersectionObserver(l=>{l.forEach(d=>{d.isIntersecting&&(setTimeout(()=>{r()},1e3),a.disconnect())})},{threshold:.5}),c=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;a.observe(c)}else r()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",n=e.adapted.body||"";return t==="inline"?`
|
|
83
|
+
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
84
|
+
${i}
|
|
85
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
|
|
86
|
+
<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" />
|
|
87
|
+
</svg>
|
|
88
|
+
</span>
|
|
89
|
+
`:t==="minimal"?`
|
|
90
|
+
<span class="ax-ad-suffix-link ax-ad-variant-minimal" data-ad-id="${e.original.id}">
|
|
91
|
+
${i}
|
|
92
|
+
</span>
|
|
93
|
+
`:`
|
|
94
|
+
<div class="ax-ad-suffix ax-ad-variant-block" data-ad-id="${e.original.id}">
|
|
95
|
+
<div class="ax-ad-suffix-content">
|
|
96
|
+
<p class="ax-ad-suffix-title">${i}</p>
|
|
97
|
+
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
|
|
98
|
+
<span class="ax-ad-sponsored-badge">Sponsored</span>
|
|
99
|
+
<svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24">
|
|
100
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
|
101
|
+
</svg>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
${n?`<p class="ax-ad-suffix-body">${n}</p>`:""}
|
|
105
|
+
</div>
|
|
106
|
+
`}static generateSponsoredSourceHTML(e,t){var r;const i=e.adapted.title||"",n=e.adapted.body||"",s=((r=e.adapted.image)==null?void 0:r.url)||"";return t==="list-item"?`
|
|
107
|
+
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
108
|
+
${s?`<img src="${s}" alt="${i}" loading="lazy" />`:""}
|
|
109
|
+
<div class="ax-ad-source-info">
|
|
110
|
+
<span class="ax-ad-source-name">${i}</span>
|
|
111
|
+
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
112
|
+
</div>
|
|
113
|
+
<svg width="12" height="12" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
|
|
114
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
115
|
+
</svg>
|
|
116
|
+
</div>
|
|
117
|
+
`:t==="minimal"?`
|
|
118
|
+
<div data-ad-id="${e.original.id}" style="display:inline-flex">
|
|
119
|
+
<a class="ax-ad-source-link ax-ad-variant-minimal">
|
|
120
|
+
${s?`<img src="${s}" alt="${i}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
|
|
121
|
+
<span>${i}</span>
|
|
122
|
+
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
123
|
+
</a>
|
|
124
|
+
</div>
|
|
125
|
+
`:`
|
|
126
|
+
<div class="ax-ad-source ax-ad-variant-card" data-ad-id="${e.original.id}">
|
|
127
|
+
${s?`<img src="${s}" alt="${i}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
|
|
128
|
+
<div class="ax-ad-source-info">
|
|
129
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
|
130
|
+
<span class="ax-ad-source-name">${i}</span>
|
|
131
|
+
<span class="ax-ad-sponsored-badge">Sponsored</span>
|
|
132
|
+
</div>
|
|
133
|
+
${n?`<p class="ax-ad-source-desc">${n}</p>`:""}
|
|
134
|
+
</div>
|
|
135
|
+
<svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
|
|
136
|
+
<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" />
|
|
137
|
+
</svg>
|
|
138
|
+
</div>
|
|
139
|
+
`}}class v{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 v),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("chatbox_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("chatbox_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 w=()=>v.getInstance();function h(o){return w().collect(o)}function N(){return h().device}function $(){return h().user}function H(){return h().app}function G(){return h().geo}function j(){return $().id}function D(){w().clearCache()}function W(o){const e=h(o);return JSON.stringify(e,null,2)}function Q(){var t;const o=h();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 z(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,impression_url:o.tracking.impressionUrl},metadata:{viewToken:o.tracking.viewToken,styling:o.adapted.styling}}}async function I(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=w().collect(),s={...o,clientInfo:n},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey);const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(s)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(!c.success)throw new Error(c.error||"Ad request failed");return c.data}class F{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 E,typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,slotCount:e.slots.length,enabled:this.enabled})}async requestAds(e){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...");try{const t=await I({conversationContext:e.conversationContext,userContext:e.userContext,slots:this.config.slots},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=t.requestId,this.adsAnalyticsMap.clear(),t.slots.forEach(n=>{n.ads.forEach((s,r)=>{const a=s.original.id;this.adsAnalyticsMap.set(a,{requestId:t.requestId,slotId:n.slotId,position:r,totalAds:n.ads.length})})});const i={};return t.slots.forEach(n=>{i[n.slotId]=n}),this.slots=i,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)}),t}catch(t){const i=t;throw this.eventBus.emit("adsError",i),i}finally{this.isLoading=!1}}render(e,t,i={}){const n=this.slots[e];if(!n||!n.ads||n.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",e),null;const s=i.adIndex??0,r=n.ads[s];if(!r)return this.config.debug&&console.warn(`[AdManager] Ad at index ${s} not found in slot:`,e),null;const a=this.adsAnalyticsMap.get(r.original.id),c=this.config.slots.find(u=>u.slotId===e),l=(c==null?void 0:c.format)||"action_card";t.innerHTML="";const d={analytics:a,variant:i.variant,onClick:i.onClick,onImpression:i.onImpression};return l==="suffix"?g.renderSuffixAd(r,t,d):l==="source"?g.renderSponsoredSource(r,t,d):l==="lead_gen"?g.renderLeadGenAd(r,t,d):g.renderActionCard(r,t,d)}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},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=p(),r=await b({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});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=p(),s=await y({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});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")}}function S(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class M{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(l=>this.handleIntersection(e,l[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 l=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!r.enteredViewportAt&&(r.enteredViewportAt=l,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${l}`),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:l})),!d&&r.enteredViewportAt){const u=l-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:l})}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(),l=a.isViewable;if(a.enteredViewportAt){const d=c-a.enteredViewportAt;a.currentVisibleTimeMs=d,d>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,l||(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,l=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,l)}}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=S();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=S();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let m=null;function J(o,e){return m||(m=new M(o,e),m.setupBeforeUnload()),m}const X="0.1.0";exports.AdManager=F;exports.AnalyticsSender=A;exports.ClientInfoCollector=v;exports.DOMRenderer=g;exports.HTMLRenderer=f;exports.SDK_VERSION=X;exports.SessionManager=k;exports.ViewabilityTracker=M;exports.adaptAdToKoahAd=z;exports.clearClientInfoCache=D;exports.createAnalytics=K;exports.createSession=R;exports.fetchAds=I;exports.generateViewToken=B;exports.getAppInfo=H;exports.getClientInfo=h;exports.getClientInfoCollector=w;exports.getClientInfoJSON=W;exports.getClientInfoSummary=Q;exports.getDeviceInfo=N;exports.getGeoInfo=G;exports.getSessionId=p;exports.getUserId=j;exports.getUserInfo=$;exports.getViewabilityTracker=J;exports.trackAdClick=b;exports.trackAdImpression=y;exports.trackClicksBatch=q;exports.trackImpressionsBatch=L;
|