@action-x/ad-sdk 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +433 -150
- package/dist/index.cjs +31 -31
- package/dist/index.d.ts +7 -140
- package/dist/index.js +270 -492
- package/dist/index.umd.js +34 -34
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class N{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class N{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const n=this.events[e].indexOf(t);n>-1&&this.events[e].splice(n,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(n=>{try{n(...t)}catch(s){console.error(`[EventEmitter] Error in event handler for "${e}":`,s)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}function I(r){return r.replace(/<[^>]+>/g," ").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'").replace(/ /g," ").replace(/\s{2,}/g," ").trim()}class w{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var h;const{variant:n="horizontal",includeWrapper:s=!0}=t,i=e.adapted,o=this.getClickUrl(e),a=this.getImpressionUrl(e),c=(h=i.image)!=null&&h.url?`<div class="ax-ad-image-wrap">
|
|
2
2
|
<img
|
|
3
|
-
src="${
|
|
4
|
-
alt="${
|
|
3
|
+
src="${i.image.url}"
|
|
4
|
+
alt="${i.title}"
|
|
5
5
|
class="ax-ad-image"
|
|
6
6
|
loading="lazy"
|
|
7
7
|
/>
|
|
8
|
-
</div>`:"",
|
|
8
|
+
</div>`:"",u=(i.brand||"").trim(),l=u&&u.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${i.brand}</span>`:"",d=`
|
|
9
9
|
${c}
|
|
10
10
|
<div class="ax-ad-content">
|
|
11
11
|
${l}
|
|
12
|
-
<h3 class="ax-ad-title">${
|
|
13
|
-
${
|
|
12
|
+
<h3 class="ax-ad-title">${i.title}</h3>
|
|
13
|
+
${i.body?`<p class="ax-ad-body">${I(i.body)}</p>`:""}
|
|
14
14
|
</div>
|
|
15
|
-
`,
|
|
15
|
+
`,f=s?`<a
|
|
16
16
|
href="${o}"
|
|
17
|
-
class="ax-ad-card ax-ad-card-${
|
|
17
|
+
class="ax-ad-card ax-ad-card-${n} ax-ad-card-link"
|
|
18
18
|
target="_blank"
|
|
19
19
|
rel="sponsored noopener noreferrer"
|
|
20
20
|
data-ad-id="${e.original.id}"
|
|
21
21
|
data-impression-url="${a}"
|
|
22
|
-
>`:"",
|
|
22
|
+
>`:"",g=s?"</a>":"";return f+d+g}static renderSuffixAd(e){const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
|
|
23
23
|
<div class="ax-ad-suffix" data-ad-id="${e.original.id}">
|
|
24
24
|
<div class="ax-ad-suffix-content">
|
|
25
25
|
${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
|
|
26
|
-
${t.body?`<p class="ax-ad-suffix-body">${
|
|
26
|
+
${t.body?`<p class="ax-ad-suffix-body">${I(t.body)}</p>`:""}
|
|
27
27
|
<a
|
|
28
|
-
href="${
|
|
28
|
+
href="${n}"
|
|
29
29
|
class="ax-ad-suffix-link"
|
|
30
30
|
target="_blank"
|
|
31
31
|
rel="sponsored noopener noreferrer"
|
|
@@ -36,31 +36,31 @@
|
|
|
36
36
|
</a>
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
|
-
`}static renderSponsoredSource(e){var
|
|
39
|
+
`}static renderSponsoredSource(e){var i;const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
|
|
40
40
|
<div class="ax-ad-source" data-ad-id="${e.original.id}">
|
|
41
41
|
<a
|
|
42
|
-
href="${
|
|
42
|
+
href="${n}"
|
|
43
43
|
class="ax-ad-source-link"
|
|
44
44
|
target="_blank"
|
|
45
45
|
rel="sponsored noopener noreferrer"
|
|
46
46
|
data-ad-id="${e.original.id}"
|
|
47
47
|
data-impression-url="${s}"
|
|
48
48
|
>
|
|
49
|
-
${(
|
|
49
|
+
${(i=t.image)!=null&&i.url?`<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />`:""}
|
|
50
50
|
<div class="ax-ad-source-info">
|
|
51
51
|
<span class="ax-ad-source-name">${t.title}</span>
|
|
52
|
-
${t.body?`<span class="ax-ad-source-desc">${
|
|
52
|
+
${t.body?`<span class="ax-ad-source-desc">${I(t.body)}</span>`:""}
|
|
53
53
|
</div>
|
|
54
54
|
<span class="ax-ad-source-badge">Sponsored</span>
|
|
55
55
|
</a>
|
|
56
56
|
</div>
|
|
57
|
-
`}static renderLeadGenAd(e){const t=e.adapted,
|
|
57
|
+
`}static renderLeadGenAd(e){const t=e.adapted,n=this.getClickUrl(e),s=this.getImpressionUrl(e);return`
|
|
58
58
|
<div class="ax-ad-leadgen" data-ad-id="${e.original.id}">
|
|
59
59
|
<div class="ax-ad-leadgen-content">
|
|
60
60
|
${t.title?`<h3 class="ax-ad-leadgen-title">${t.title}</h3>`:""}
|
|
61
|
-
${t.body?`<p class="ax-ad-leadgen-body">${
|
|
61
|
+
${t.body?`<p class="ax-ad-leadgen-body">${I(t.body)}</p>`:""}
|
|
62
62
|
<a
|
|
63
|
-
href="${
|
|
63
|
+
href="${n}"
|
|
64
64
|
class="ax-ad-leadgen-cta"
|
|
65
65
|
target="_blank"
|
|
66
66
|
rel="sponsored noopener noreferrer"
|
|
@@ -71,22 +71,22 @@
|
|
|
71
71
|
</a>
|
|
72
72
|
</div>
|
|
73
73
|
</div>
|
|
74
|
-
`}static renderAds(e,t=
|
|
75
|
-
`)}}const
|
|
74
|
+
`}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
|
|
75
|
+
`)}}const H="ad_session_id";function G(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}function D(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function T(r){if(r)return r;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class ${constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||H,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const F=new $,k=()=>F.getSessionId();class A{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${T(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${T(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,n){let s=null;for(let i=0;i<=this.retryAttempts;i++){const o=this.debug||D();try{o&&(console.log(`[Analytics] Request URL (${n}):`,e),console.log(`[Analytics] Sending ${n} event (attempt ${i+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),u={"Content-Type":"application/json"},l=G();l&&(u["X-API-Key"]=l,o&&console.log(`[Analytics] Adding X-API-Key header for ${n} event`));const d=await fetch(e,{method:"POST",headers:u,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!d.ok)throw new Error(`HTTP ${d.status}: ${d.statusText}`);const f=await d.json();return o&&console.log(`[Analytics] ${n} event tracked successfully:`,f),f}catch(a){s=a,o&&console.warn(`[Analytics] ${n} tracking failed (attempt ${i+1}):`,a),i<this.retryAttempts&&await this.delay(this.retryDelay*(i+1))}}return console.error(`[Analytics] ${n} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new A,x=(r,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackImpression(r):O.trackImpression(r),_=(r,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackClick(r):O.trackClick(r);function V(r,e){return`vt_${r}_${e}`}async function j(r){return Promise.all(r.map(e=>x(e)))}async function z(r){return Promise.all(r.map(e=>_(e)))}function J(r){const e=new A(r);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function W(r={}){const e=new $(r);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const M=class M{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,n={}){t.innerHTML=w.renderActionCard(e,n);const s=t.querySelector(".ax-ad-card-link");return s&&s.addEventListener("click",i=>{i.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderSuffixAd(e,t,n={}){const s=n.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const i=t.querySelector(".ax-ad-suffix-link");return i&&i.addEventListener("click",o=>{o.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderSponsoredSource(e,t,n={}){const s=n.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const i=t.querySelector(".ax-ad-source-link");return i&&i.addEventListener("click",o=>{o.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderLeadGenAd(e,t,n={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",i=>{i.preventDefault(),this.handleClick(e,n)}),this.trackImpression(e,t,n),t}static renderAds(e,t,n=this.renderActionCard.bind(this),s){t.innerHTML="";const i=document.createElement("div");return i.className="ax-ads-container",e.forEach(o=>{const a=document.createElement("div");a.className="ax-ad-wrapper",n.call(this,o,a,s),i.appendChild(a)}),t.appendChild(i),t}static handleClick(e,t){const{onClick:n}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}n&&n(e),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,n){const{analytics:s,onImpression:i}=n,o=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(o))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(o)||(a=!0,this.trackedImpressionKeys.add(o),u())},u=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:k(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{i&&i(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):i&&i(e)};if(typeof IntersectionObserver<"u"){let l=!1,d=null;const f=new IntersectionObserver(h=>{h.forEach(v=>{l=v.isIntersecting&&v.intersectionRatio>=.5,l?d||(d=setTimeout(()=>{d=null,l&&(c(),f.disconnect())},1e3)):d&&(clearTimeout(d),d=null)})},{threshold:.5}),g=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(g)}else c()}static generateSuffixAdHTML(e,t){const n=e.adapted.title||"",s=I(e.adapted.body||"");return t==="inline"?`
|
|
76
76
|
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
77
|
-
${
|
|
77
|
+
${n}
|
|
78
78
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
|
|
79
79
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
80
80
|
</svg>
|
|
81
81
|
</span>
|
|
82
82
|
`:t==="minimal"?`
|
|
83
83
|
<span class="ax-ad-suffix-link ax-ad-variant-minimal" data-ad-id="${e.original.id}">
|
|
84
|
-
${
|
|
84
|
+
${n}
|
|
85
85
|
</span>
|
|
86
86
|
`:`
|
|
87
87
|
<div class="ax-ad-suffix ax-ad-variant-block" data-ad-id="${e.original.id}">
|
|
88
88
|
<div class="ax-ad-suffix-content">
|
|
89
|
-
<p class="ax-ad-suffix-title">${
|
|
89
|
+
<p class="ax-ad-suffix-title">${n}</p>
|
|
90
90
|
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
|
|
91
91
|
<span class="ax-ad-sponsored-badge">Sponsored</span>
|
|
92
92
|
<svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24">
|
|
@@ -96,11 +96,11 @@
|
|
|
96
96
|
</div>
|
|
97
97
|
${s?`<p class="ax-ad-suffix-body">${s}</p>`:""}
|
|
98
98
|
</div>
|
|
99
|
-
`}static generateSponsoredSourceHTML(e,t){var o;const
|
|
99
|
+
`}static generateSponsoredSourceHTML(e,t){var o;const n=e.adapted.title||"",s=I(e.adapted.body||""),i=((o=e.adapted.image)==null?void 0:o.url)||"";return t==="list-item"?`
|
|
100
100
|
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
101
|
-
${
|
|
101
|
+
${i?`<img src="${i}" alt="${n}" loading="lazy" />`:""}
|
|
102
102
|
<div class="ax-ad-source-info">
|
|
103
|
-
<span class="ax-ad-source-name">${
|
|
103
|
+
<span class="ax-ad-source-name">${n}</span>
|
|
104
104
|
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
105
105
|
</div>
|
|
106
106
|
<svg width="12" height="12" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
|
|
@@ -110,17 +110,17 @@
|
|
|
110
110
|
`:t==="minimal"?`
|
|
111
111
|
<div data-ad-id="${e.original.id}" style="display:inline-flex">
|
|
112
112
|
<a class="ax-ad-source-link ax-ad-variant-minimal">
|
|
113
|
-
${
|
|
114
|
-
<span>${
|
|
113
|
+
${i?`<img src="${i}" alt="${n}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />`:""}
|
|
114
|
+
<span>${n}</span>
|
|
115
115
|
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
116
116
|
</a>
|
|
117
117
|
</div>
|
|
118
118
|
`:`
|
|
119
119
|
<div class="ax-ad-source ax-ad-variant-card" data-ad-id="${e.original.id}">
|
|
120
|
-
${
|
|
120
|
+
${i?`<img src="${i}" alt="${n}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />`:""}
|
|
121
121
|
<div class="ax-ad-source-info">
|
|
122
122
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
|
123
|
-
<span class="ax-ad-source-name">${
|
|
123
|
+
<span class="ax-ad-source-name">${n}</span>
|
|
124
124
|
<span class="ax-ad-sponsored-badge">Sponsored</span>
|
|
125
125
|
</div>
|
|
126
126
|
${s?`<p class="ax-ad-source-desc">${s}</p>`:""}
|
|
@@ -129,4 +129,4 @@
|
|
|
129
129
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
130
130
|
</svg>
|
|
131
131
|
</div>
|
|
132
|
-
`}};_.trackedImpressionKeys=new Set;let m=_;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,n;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const i=(n=document.querySelector('meta[name="application-name"]'))==null?void 0:n.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const s=i.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var i,s;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const n=e.match(/Mac OS X (\d+[._]\d+)/);return n?n[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const n=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(n)return n[1]+" "+n[2];if(/OnePlus/.test(e)){const o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function y(r){return T().collect(r)}function X(){return y().device}function q(){return y().user}function Z(){return y().app}function Y(){return y().geo}function L(){return q().id}function ee(){T().clearCache()}function te(r){const e=y(r);return JSON.stringify(e,null,2)}function ie(){var t;const r=y();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function E(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function se(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function R(r,e={}){const t=e.apiBaseUrl||"/api/v1",s=T().collect(),n={...r,clientInfo:s},o={"Content-Type":"application/json"};e.apiKey&&(o["X-API-Key"]=e.apiKey),E()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",r),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:o,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(E()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const ne={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function P(r={}){const e={variant:r.variant??C.variant,count:r.count??C.count,preferences:{...C.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ne,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new N,this.slots_config=P(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),s=g.inFlightRequests.get(i);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const n=(async()=>{try{const o=await R({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??w()}:{userId:this.currentUserId,sessionId:w()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=o.requestId,this.adsAnalyticsMap.clear(),o.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:o.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return o.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),o}catch(o){const a=o;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,n),n}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return L()}catch{return}}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,o=s?e:t,a=(s?t:i)||{};if(!o)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[n];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",n),null;const d=a.adIndex??0,l=c.ads[d];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,n),null;const u=this.adsAnalyticsMap.get(l.original.id),h=this.slots_config.find(A=>A.slotId===n),p=(h==null?void 0:h.format)||"action_card";o.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?m.renderSuffixAd(l,o,f):p==="source"?m.renderSponsoredSource(l,o,f):p==="lead_gen"?m.renderLeadGenAd(l,o,f):m.renderActionCard(l,o,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=P(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const s=this.adsAnalyticsMap.get(i);s&&t.set(i,s)}),t}async trackAdClick(e,t,i){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=w(),o=await M({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:n,slotId:s.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return o&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=w(),n=await I({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return n&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",n.eventId)),n}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let $=g;function O(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}class K{constructor(e={},t="/api/v1"){var i,s;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((s=e.batchConfig)==null?void 0:s.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,s,n){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const o={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,o),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,s,n),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,s,n);const c=setTimeout(()=>{this.endTracking(e,i,s,n)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,s,n){const o=this.metrics.get(e);if(!o||!this.isTracking.get(e))return;const a=o.isViewable,c=Math.round(t.intersectionRatio*100);o.visiblePercentage=c,o.maxVisiblePercentage=Math.max(o.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!o.enteredViewportAt&&(o.enteredViewportAt=d,o.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&o.enteredViewportAt){const u=d-o.enteredViewportAt;o.totalVisibleTimeMs+=u,o.enteredViewportAt=null,o.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${o.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:o.isViewable,timestamp:d})}o.isViewable!==a&&o.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,o)}startMonitoring(e,t,i,s){const o=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(o);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const l=c-a.enteredViewportAt;a.currentVisibleTimeMs=l,l>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,o)}queueEvent(e,t){var n,o;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const s=((n=this.config.batchConfig)==null?void 0:n.maxBatchSize)??5;if(i.length>=s)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((o=this.config.batchConfig)==null?void 0:o.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const s={"Content-Type":"application/json"},n=O();n&&(s["x-api-key"]=n),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:s,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(s){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,s),t.unshift(...i)}}endTracking(e,t,i,s){const n=this.metrics.get(e);if(!n)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const o=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"end_tracking",visiblePercentage:n.visiblePercentage,maxVisiblePercentage:n.maxVisiblePercentage,totalVisibleTimeMs:n.totalVisibleTimeMs,isViewable:n.isViewable,timestamp:o}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const s=this.timers.get(`${e}_monitoring`);s&&(clearInterval(s),this.timers.delete(`${e}_monitoring`));const n=this.batchTimers.get(e);n&&(clearTimeout(n),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},s=O();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let x=null;function re(r,e){return x||(x=new K(r,e),x.setupBeforeUnload()),x}const oe="0.1.0";exports.AdManager=$;exports.AnalyticsSender=k;exports.ClientInfoCollector=S;exports.DOMRenderer=m;exports.HTMLRenderer=v;exports.SDK_VERSION=oe;exports.SessionManager=U;exports.ViewabilityTracker=K;exports.adaptAdToKoahAd=se;exports.clearClientInfoCache=ee;exports.createAnalytics=Q;exports.createSession=J;exports.fetchAds=R;exports.generateViewToken=j;exports.getAppInfo=Z;exports.getClientInfo=y;exports.getClientInfoCollector=T;exports.getClientInfoJSON=te;exports.getClientInfoSummary=ie;exports.getDeviceInfo=X;exports.getGeoInfo=Y;exports.getSessionId=w;exports.getUserId=L;exports.getUserInfo=q;exports.getViewabilityTracker=re;exports.trackAdClick=M;exports.trackAdImpression=I;exports.trackClicksBatch=W;exports.trackImpressionsBatch=z;
|
|
132
|
+
`}};M.trackedImpressionKeys=new Set;let m=M;class b{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),n=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:n,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,i;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const n=(i=document.querySelector('meta[name="application-name"]'))==null?void 0:i.getAttribute("content");return n||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const n=document.querySelector(t);if(n){if(t.includes("manifest"))return"1.0.0";const s=n.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var n,s;if(typeof document>"u")return"unknown";const e=(n=document.querySelector('meta[name="publisher-id"]'))==null?void 0:n.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new b),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const n=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:n.connectiontype??0,bandwidth:n.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),n=this.detectOSVersion(e);return{ua:e,os:t,osv:n,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const n=e.match(/Android (\d+)\.(\d+)/);if(n)return`${n[1]}.${n[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const i=e.match(/Mac OS X (\d+[._]\d+)/);return i?i[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const n=e.match(/Samsung-.*(SM-\w+)/);if(n)return n[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const i=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(i)return i[1]+" "+i[2];if(/OnePlus/.test(e)){const o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const n={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&n[t])return n[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const S=()=>b.getInstance();function y(r){return S().collect(r)}function X(){return y().device}function L(){return y().user}function Z(){return y().app}function Y(){return y().geo}function P(){return L().id}function Q(){S().clearCache()}function ee(r){const e=y(r);return JSON.stringify(e,null,2)}function te(){var t;const r=y();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function q(){if(typeof crypto>"u"||!crypto.subtle)throw new Error("Web Crypto API not supported")}async function K(r,e){q();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),n=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["encrypt"]),s=crypto.getRandomValues(new Uint8Array(12)),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:s},n,new TextEncoder().encode(r)),o=new Uint8Array(s.length+i.byteLength);return o.set(s),o.set(new Uint8Array(i),s.length),btoa(String.fromCharCode(...o))}async function B(r,e){q();const t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),n=await crypto.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]),s=Uint8Array.from(atob(r),c=>c.charCodeAt(0)),i=s.slice(0,12),o=s.slice(12),a=await crypto.subtle.decrypt({name:"AES-GCM",iv:i},n,o);return new TextDecoder().decode(a)}function ne(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function se(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function R(r,e={}){const t=e.apiBaseUrl||"/api/v1",n=ne(),i=S().collect(),o={...r,clientInfo:i},a={"Content-Type":"application/json"};e.apiKey&&(a["X-API-Key"]=e.apiKey);let c;if(e.apiKey&&!n)try{c={encryptedPayload:await K(JSON.stringify(o),e.apiKey)}}catch(f){n&&console.warn("[fetchAds] Encryption failed, using plaintext:",f),c=o}else c=o;n&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request body:",c));const u=await fetch(`${t}/ads/request`,{method:"POST",headers:a,body:JSON.stringify(c)});if(!u.ok)throw new Error(`Ad request failed: ${u.status} ${u.statusText}`);const l=await u.json();if(!l.success)throw new Error(l.error||"Ad request failed");let d=l.data;if(l.data.encryptedPayload&&e.apiKey){const f=await B(l.data.encryptedPayload,e.apiKey);d=JSON.parse(f)}return n&&console.log("[fetchAds] Response:",d),d}const ie={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function E(r={}){const e={variant:r.variant??C.variant,count:r.count??C.count,preferences:{...C.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ie,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const p=class p{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new N,this.slots_config=E(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const n=this.buildRequestKey(t),s=p.inFlightRequests.get(n);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const i=(async()=>{try{const o=await R({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??k()}:{userId:this.currentUserId,sessionId:k()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=o.requestId,this.adsAnalyticsMap.clear(),o.slots.forEach(c=>{c.ads.forEach((u,l)=>{const d=u.original.id;this.adsAnalyticsMap.set(d,{requestId:o.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return o.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),o}catch(o){const a=o;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,p.inFlightRequests.delete(n)}})();return p.inFlightRequests.set(n,i),i}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return P()}catch{return}}render(e,t,n={}){const s=typeof e!="string",i=s?p.DEFAULT_SLOT_ID:e,o=s?e:t,a=(s?t:n)||{};if(!o)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[i];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",i),null;const u=a.adIndex??0,l=c.ads[u];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,i),null;const d=this.adsAnalyticsMap.get(l.original.id),f=this.slots_config.find(v=>v.slotId===i),g=(f==null?void 0:f.format)||"action_card";o.innerHTML="";const h={analytics:d,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return g==="suffix"?m.renderSuffixAd(l,o,h):g==="source"?m.renderSponsoredSource(l,o,h):g==="lead_gen"?m.renderLeadGenAd(l,o,h):m.renderActionCard(l,o,h)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var n;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((n=t.ads)==null?void 0:n.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=E(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(n=>{const s=this.adsAnalyticsMap.get(n);s&&t.set(n,s)}),t}async trackAdClick(e,t,n){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const i=k(),o=await _({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:i,slotId:s.slotId,adTitle:n==null?void 0:n.title,format:n==null?void 0:n.format,source:n==null?void 0:n.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return o&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}async trackAdImpressionAPI(e,t){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=k(),i=await x({requestId:this.currentRequestId,adId:e,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return i&&(this.eventBus.emit("adImpression",e,n.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",i.eventId)),i}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let U=p;const re="0.1.0";exports.AdManager=U;exports.AnalyticsSender=A;exports.ClientInfoCollector=b;exports.DOMRenderer=m;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=$;exports.adaptAdToKoahAd=se;exports.clearClientInfoCache=Q;exports.createAnalytics=J;exports.createSession=W;exports.decryptAES=B;exports.encryptAES=K;exports.fetchAds=R;exports.generateViewToken=V;exports.getAppInfo=Z;exports.getClientInfo=y;exports.getClientInfoCollector=S;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=X;exports.getGeoInfo=Y;exports.getSessionId=k;exports.getUserId=P;exports.getUserInfo=L;exports.trackAdClick=_;exports.trackAdImpression=x;exports.trackClicksBatch=z;exports.trackImpressionsBatch=j;
|
package/dist/index.d.ts
CHANGED
|
@@ -680,6 +680,8 @@ export declare interface DecisionResult {
|
|
|
680
680
|
routing: RoutingInfo;
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
+
export declare function decryptAES(base64: string, apiKey: string): Promise<string>;
|
|
684
|
+
|
|
683
685
|
/**
|
|
684
686
|
* Device Information (auto-collected by SDK)
|
|
685
687
|
*/
|
|
@@ -766,6 +768,11 @@ export declare class DOMRenderer {
|
|
|
766
768
|
private static generateSponsoredSourceHTML;
|
|
767
769
|
}
|
|
768
770
|
|
|
771
|
+
/**
|
|
772
|
+
* AES-GCM encryption/decryption using Web Crypto API
|
|
773
|
+
*/
|
|
774
|
+
export declare function encryptAES(text: string, apiKey: string): Promise<string>;
|
|
775
|
+
|
|
769
776
|
/**
|
|
770
777
|
* Enhanced Content Props
|
|
771
778
|
* Props for the EnhancedContent component
|
|
@@ -1018,11 +1025,6 @@ export declare function getUserId(): string;
|
|
|
1018
1025
|
*/
|
|
1019
1026
|
export declare function getUserInfo(): UserInfo;
|
|
1020
1027
|
|
|
1021
|
-
/**
|
|
1022
|
-
* Get or create the viewability tracker singleton
|
|
1023
|
-
*/
|
|
1024
|
-
export declare function getViewabilityTracker(config?: ViewabilityConfig, baseUrl?: string): ViewabilityTracker;
|
|
1025
|
-
|
|
1026
1028
|
/**
|
|
1027
1029
|
* Global Suggestions
|
|
1028
1030
|
*/
|
|
@@ -1451,139 +1453,4 @@ export declare interface UserProfile {
|
|
|
1451
1453
|
};
|
|
1452
1454
|
}
|
|
1453
1455
|
|
|
1454
|
-
export declare interface ViewabilityConfig {
|
|
1455
|
-
/** Minimum visible percentage to consider "in view" (default: 50) */
|
|
1456
|
-
minVisiblePercentage?: number;
|
|
1457
|
-
/** Minimum duration in ms to consider "viewable" (default: 1000) */
|
|
1458
|
-
minViewableDuration?: number;
|
|
1459
|
-
/** Maximum tracking duration in ms (default: 60000 = 1 minute) */
|
|
1460
|
-
maxTrackingDuration?: number;
|
|
1461
|
-
/** Batch events before sending (default: 5 events or 10 seconds) */
|
|
1462
|
-
batchConfig?: {
|
|
1463
|
-
maxBatchSize?: number;
|
|
1464
|
-
maxBatchWaitMs?: number;
|
|
1465
|
-
};
|
|
1466
|
-
/** Enable debug logging (default: false) */
|
|
1467
|
-
debug?: boolean;
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
export declare interface ViewabilityEventData {
|
|
1471
|
-
adId: string;
|
|
1472
|
-
sessionId: string;
|
|
1473
|
-
requestId?: string;
|
|
1474
|
-
viewToken?: string;
|
|
1475
|
-
eventType: ViewabilityEventType;
|
|
1476
|
-
visiblePercentage: number;
|
|
1477
|
-
maxVisiblePercentage: number;
|
|
1478
|
-
totalVisibleTimeMs: number;
|
|
1479
|
-
isViewable: boolean;
|
|
1480
|
-
viewportWidth?: number;
|
|
1481
|
-
viewportHeight?: number;
|
|
1482
|
-
elementWidth?: number;
|
|
1483
|
-
elementHeight?: number;
|
|
1484
|
-
positionX?: number;
|
|
1485
|
-
positionY?: number;
|
|
1486
|
-
timestamp: number;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
/**
|
|
1490
|
-
* Viewability Tracking Module (IAB/MRC Compliant)
|
|
1491
|
-
*
|
|
1492
|
-
* IAB/MRC Standard:
|
|
1493
|
-
* - Viewable: >50% of ad pixels in view for >1 second
|
|
1494
|
-
* - Uses IntersectionObserver API for efficient tracking
|
|
1495
|
-
*
|
|
1496
|
-
* Event-driven approach: Only sends data on state changes, not periodic sampling
|
|
1497
|
-
*/
|
|
1498
|
-
export declare type ViewabilityEventType = 'enter_viewport' | 'become_viewable' | 'update_viewability' | 'exit_viewport' | 'end_tracking';
|
|
1499
|
-
|
|
1500
|
-
export declare interface ViewabilityMetrics {
|
|
1501
|
-
/** Current visible percentage (0-100) */
|
|
1502
|
-
visiblePercentage: number;
|
|
1503
|
-
/** Maximum visible percentage observed */
|
|
1504
|
-
maxVisiblePercentage: number;
|
|
1505
|
-
/** Total time visible in ms */
|
|
1506
|
-
totalVisibleTimeMs: number;
|
|
1507
|
-
/** Current continuous visible time in ms */
|
|
1508
|
-
currentVisibleTimeMs: number;
|
|
1509
|
-
/** Whether meets IAB/MRC viewable criteria */
|
|
1510
|
-
isViewable: boolean;
|
|
1511
|
-
/** Time when became viewable (null if not viewable yet) */
|
|
1512
|
-
viewableAt: number | null;
|
|
1513
|
-
/** Time when entered viewport (null if never entered) */
|
|
1514
|
-
enteredViewportAt: number | null;
|
|
1515
|
-
/** Total number of times entered viewport */
|
|
1516
|
-
enterCount: number;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
/**
|
|
1520
|
-
* ViewabilityTracker class for tracking ad viewability
|
|
1521
|
-
* Event-driven: Only sends data on state changes
|
|
1522
|
-
*/
|
|
1523
|
-
export declare class ViewabilityTracker {
|
|
1524
|
-
private observers;
|
|
1525
|
-
private metrics;
|
|
1526
|
-
private timers;
|
|
1527
|
-
private batchTimers;
|
|
1528
|
-
private eventQueue;
|
|
1529
|
-
private config;
|
|
1530
|
-
private baseUrl;
|
|
1531
|
-
private isTracking;
|
|
1532
|
-
constructor(config?: ViewabilityConfig, baseUrl?: string);
|
|
1533
|
-
/**
|
|
1534
|
-
* Start tracking viewability for an ad element
|
|
1535
|
-
*/
|
|
1536
|
-
startTracking(adId: string, element: HTMLElement, sessionId: string, requestId?: string, viewToken?: string): void;
|
|
1537
|
-
/**
|
|
1538
|
-
* Stop tracking an ad element
|
|
1539
|
-
*/
|
|
1540
|
-
stopTracking(adId: string): void;
|
|
1541
|
-
/**
|
|
1542
|
-
* Get current metrics for an ad
|
|
1543
|
-
*/
|
|
1544
|
-
getMetrics(adId: string): ViewabilityMetrics | undefined;
|
|
1545
|
-
/**
|
|
1546
|
-
* Handle IntersectionObserver callback
|
|
1547
|
-
* Event-driven: Only process when state actually changes
|
|
1548
|
-
*/
|
|
1549
|
-
private handleIntersection;
|
|
1550
|
-
/**
|
|
1551
|
-
* Start monitoring loop for tracking duration
|
|
1552
|
-
* Runs every 100ms to track continuous visible time
|
|
1553
|
-
*/
|
|
1554
|
-
private startMonitoring;
|
|
1555
|
-
/**
|
|
1556
|
-
* Queue an event to be sent (batched)
|
|
1557
|
-
*/
|
|
1558
|
-
private queueEvent;
|
|
1559
|
-
/**
|
|
1560
|
-
* Flush the event queue and send to server
|
|
1561
|
-
*/
|
|
1562
|
-
private flushQueue;
|
|
1563
|
-
/**
|
|
1564
|
-
* End tracking and send final metrics
|
|
1565
|
-
*/
|
|
1566
|
-
private endTracking;
|
|
1567
|
-
/**
|
|
1568
|
-
* Cleanup resources for an ad
|
|
1569
|
-
*/
|
|
1570
|
-
private cleanup;
|
|
1571
|
-
/**
|
|
1572
|
-
* Create thresholds for IntersectionObserver
|
|
1573
|
-
*/
|
|
1574
|
-
private createThresholds;
|
|
1575
|
-
/**
|
|
1576
|
-
* Stop all tracking
|
|
1577
|
-
*/
|
|
1578
|
-
stopAll(): void;
|
|
1579
|
-
/**
|
|
1580
|
-
* Debug logging
|
|
1581
|
-
*/
|
|
1582
|
-
private log;
|
|
1583
|
-
/**
|
|
1584
|
-
* Send any pending events before page unload
|
|
1585
|
-
*/
|
|
1586
|
-
setupBeforeUnload(): void;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
1456
|
export { }
|