@action-x/ad-sdk 0.1.0 → 0.1.3
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 +160 -588
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +30 -13
- package/dist/index.js +163 -149
- package/dist/index.umd.js +6 -6
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class L{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 p{static renderActionCard(e,t={}){var h;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=(h=s.image)!=null&&h.url?`<img
|
|
2
2
|
src="${s.image.url}"
|
|
3
3
|
alt="${s.title}"
|
|
4
4
|
class="ax-ad-image"
|
|
5
5
|
loading="lazy"
|
|
6
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
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>`:"",
|
|
8
|
+
</div>`:"",u=s.brand?`<span class="ax-ad-brand">${s.brand}</span>`:"",m=`
|
|
9
9
|
${c}
|
|
10
10
|
<div class="ax-ad-content">
|
|
11
11
|
${u}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</a>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
|
-
`,
|
|
29
|
+
`,y=n?"</div>":"";return a+m+y}static renderSuffixAd(e){const t=e.adapted,i=e.tracking;return`
|
|
30
30
|
<div class="ax-ad-suffix" data-ad-id="${e.original.id}">
|
|
31
31
|
<div class="ax-ad-suffix-content">
|
|
32
32
|
${t.title?`<h4 class="ax-ad-suffix-title">${t.title}</h4>`:""}
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
</a>
|
|
79
79
|
</div>
|
|
80
80
|
</div>
|
|
81
|
-
`}static renderAds(e,t=
|
|
82
|
-
`)}}const
|
|
81
|
+
`}static renderAds(e,t=p.renderActionCard.bind(p)){return e.map(t).join(`
|
|
82
|
+
`)}}const B="/api/v1/ads",q="ad_session_id";function K(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class ${constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||q,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 R=new $,f=()=>R.getSessionId();class M{constructor(e={}){this.baseUrl=e.baseUrl||B,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=K();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 E=new M,k=o=>E.trackImpression(o),A=o=>E.trackClick(o);function N(o,e){return`vt_${o}_${e}`}async function H(o){return Promise.all(o.map(e=>k(e)))}async function D(o){return Promise.all(o.map(e=>A(e)))}function G(o){const e=new M(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function j(o={}){const e=new $(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}class v{static renderActionCard(e,t,i={}){t.innerHTML=p.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=p.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&&A({requestId:i.requestId,adId:e.original.id,destinationUrl:e.tracking.clickUrl,slotId:i.slotId,sessionId:f(),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?k({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:f(),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
83
|
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
84
84
|
${i}
|
|
85
85
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
|
|
@@ -136,4 +136,4 @@
|
|
|
136
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
137
|
</svg>
|
|
138
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;
|
|
139
|
+
`}}class x{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 x),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 S=()=>x.getInstance();function g(o){return S().collect(o)}function F(){return g().device}function _(){return g().user}function z(){return g().app}function W(){return g().geo}function Q(){return _().id}function J(){S().clearCache()}function X(o){const e=g(o);return JSON.stringify(e,null,2)}function Z(){var t;const o=g();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 Y(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 P(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=S().collect(),s={...o,clientInfo:n},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey);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}const ee={width:400,height:200},T={variant:"horizontal",count:1,preferences:{}};function C(o={}){const e={variant:o.variant??T.variant,count:o.count??T.count,preferences:{...T.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ee,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const w=class w{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 L,this.slots_config=C(e.cardOption),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,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...");const t="conversationContext"in e?e:{conversationContext:e};try{const i=await P({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,sessionId:t.userContext.sessionId??f()}:{sessionId:f()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=i.requestId,this.adsAnalyticsMap.clear(),i.slots.forEach(s=>{s.ads.forEach((r,a)=>{const c=r.original.id;this.adsAnalyticsMap.set(c,{requestId:i.requestId,slotId:s.slotId,position:a,totalAds:s.ads.length})})});const n={};return i.slots.forEach(s=>{n[s.slotId]=s}),this.slots=n,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)}),i}catch(i){const n=i;throw this.eventBus.emit("adsError",n),n}finally{this.isLoading=!1}}render(e,t,i={}){const n=typeof e!="string",s=n?w.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const l=a.adIndex??0,d=c.ads[l];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${l} not found in slot:`,s),null;const u=this.adsAnalyticsMap.get(d.original.id),m=this.slots_config.find(O=>O.slotId===s),y=(m==null?void 0:m.format)||"action_card";r.innerHTML="";const h={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return y==="suffix"?v.renderSuffixAd(d,r,h):y==="source"?v.renderSponsoredSource(d,r,h):y==="lead_gen"?v.renderLeadGenAd(d,r,h):v.renderActionCard(d,r,h)}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=C(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const n=this.adsAnalyticsMap.get(i);n&&t.set(i,n)}),t}async trackAdClick(e,t,i){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=f(),r=await A({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=f(),s=await k({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")}};w.DEFAULT_SLOT_ID="action_card";let I=w;function V(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class U{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=V();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=V();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let b=null;function te(o,e){return b||(b=new U(o,e),b.setupBeforeUnload()),b}const ie="0.1.0";exports.AdManager=I;exports.AnalyticsSender=M;exports.ClientInfoCollector=x;exports.DOMRenderer=v;exports.HTMLRenderer=p;exports.SDK_VERSION=ie;exports.SessionManager=$;exports.ViewabilityTracker=U;exports.adaptAdToKoahAd=Y;exports.clearClientInfoCache=J;exports.createAnalytics=G;exports.createSession=j;exports.fetchAds=P;exports.generateViewToken=N;exports.getAppInfo=z;exports.getClientInfo=g;exports.getClientInfoCollector=S;exports.getClientInfoJSON=X;exports.getClientInfoSummary=Z;exports.getDeviceInfo=F;exports.getGeoInfo=W;exports.getSessionId=f;exports.getUserId=Q;exports.getUserInfo=_;exports.getViewabilityTracker=te;exports.trackAdClick=A;exports.trackAdImpression=k;exports.trackClicksBatch=D;exports.trackImpressionsBatch=H;
|
package/dist/index.d.ts
CHANGED
|
@@ -91,9 +91,11 @@ export declare type AdFormat = 'action_card' | 'suffix' | 'followup' | 'source'
|
|
|
91
91
|
export declare class AdManager {
|
|
92
92
|
private config;
|
|
93
93
|
private slots;
|
|
94
|
+
private slots_config;
|
|
94
95
|
private eventBus;
|
|
95
96
|
private isLoading;
|
|
96
97
|
private enabled;
|
|
98
|
+
private static readonly DEFAULT_SLOT_ID;
|
|
97
99
|
private currentRequestId;
|
|
98
100
|
private adsAnalyticsMap;
|
|
99
101
|
constructor(config: AdManagerConfig);
|
|
@@ -101,9 +103,10 @@ export declare class AdManager {
|
|
|
101
103
|
* Request ads for all configured slots
|
|
102
104
|
* ClientInfo is automatically collected and injected
|
|
103
105
|
*
|
|
104
|
-
* @param context -
|
|
106
|
+
* @param context - Simplified conversation context or full request context
|
|
105
107
|
* @returns Promise resolving to ad response batch
|
|
106
108
|
*/
|
|
109
|
+
requestAds(context: ConversationContext): Promise<AdResponseBatch>;
|
|
107
110
|
requestAds(context: {
|
|
108
111
|
conversationContext: ConversationContext;
|
|
109
112
|
userContext?: UserContext;
|
|
@@ -111,11 +114,17 @@ export declare class AdManager {
|
|
|
111
114
|
/**
|
|
112
115
|
* Render a single ad from a slot into a container element
|
|
113
116
|
*
|
|
114
|
-
* @param slotId - Slot ID to render
|
|
117
|
+
* @param slotId - Slot ID to render (optional, defaults to action_card)
|
|
115
118
|
* @param container - DOM element to render into
|
|
116
119
|
* @param options - Rendering options
|
|
117
120
|
* @returns Rendered HTMLElement or null if slot/ad not found
|
|
118
121
|
*/
|
|
122
|
+
render(container: HTMLElement, options?: {
|
|
123
|
+
adIndex?: number;
|
|
124
|
+
variant?: 'horizontal' | 'vertical' | 'compact' | 'block' | 'inline' | 'minimal' | 'card' | 'list-item';
|
|
125
|
+
onClick?: (ad: any) => void;
|
|
126
|
+
onImpression?: (ad: any) => void;
|
|
127
|
+
}): HTMLElement | null;
|
|
119
128
|
render(slotId: string, container: HTMLElement, options?: {
|
|
120
129
|
adIndex?: number;
|
|
121
130
|
variant?: 'horizontal' | 'vertical' | 'compact' | 'block' | 'inline' | 'minimal' | 'card' | 'list-item';
|
|
@@ -204,18 +213,15 @@ export declare interface AdManagerConfig {
|
|
|
204
213
|
/** Base URL for API requests */
|
|
205
214
|
apiBaseUrl: string;
|
|
206
215
|
/** API Key for authentication */
|
|
207
|
-
apiKey
|
|
208
|
-
/**
|
|
209
|
-
|
|
216
|
+
apiKey: string;
|
|
217
|
+
/** Action card display options (merged with defaults) */
|
|
218
|
+
cardOption?: CardOption;
|
|
210
219
|
/** Whether ads are enabled */
|
|
211
220
|
enabled?: boolean;
|
|
212
221
|
/** Enable debug logging */
|
|
213
222
|
debug?: boolean;
|
|
214
223
|
}
|
|
215
224
|
|
|
216
|
-
/**
|
|
217
|
-
* Events emitted by AdManager
|
|
218
|
-
*/
|
|
219
225
|
export declare type AdManagerEvents = {
|
|
220
226
|
'adsUpdated': (slots: Record<string, AdSlotResponse>) => void;
|
|
221
227
|
'adsLoading': () => void;
|
|
@@ -407,6 +413,19 @@ export declare interface AppInfo {
|
|
|
407
413
|
*/
|
|
408
414
|
export declare type BadgeStyle = 'hover' | 'subtle' | 'explicit' | 'none';
|
|
409
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Action Card display options
|
|
418
|
+
* Merged with defaults when building the slot request
|
|
419
|
+
*/
|
|
420
|
+
declare interface CardOption {
|
|
421
|
+
/** Card layout variant */
|
|
422
|
+
variant?: ActionCardVariant;
|
|
423
|
+
/** Number of ads to request */
|
|
424
|
+
count?: number;
|
|
425
|
+
/** Content preferences */
|
|
426
|
+
preferences?: AdSlotPreferences;
|
|
427
|
+
}
|
|
428
|
+
|
|
410
429
|
/**
|
|
411
430
|
* Chat Completion Request
|
|
412
431
|
*/
|
|
@@ -612,9 +631,7 @@ export declare interface ClientInfoOptions {
|
|
|
612
631
|
*/
|
|
613
632
|
export declare interface ConversationContext {
|
|
614
633
|
query: string;
|
|
615
|
-
response
|
|
616
|
-
intent?: IntentResult;
|
|
617
|
-
conversationHistory?: ConversationMessage[];
|
|
634
|
+
response: string;
|
|
618
635
|
}
|
|
619
636
|
|
|
620
637
|
/**
|
|
@@ -1362,7 +1379,7 @@ export declare interface UseAdSlotsResult {
|
|
|
1362
1379
|
slots: Record<string, AdSlotResponse>;
|
|
1363
1380
|
isLoading: boolean;
|
|
1364
1381
|
error: Error | null;
|
|
1365
|
-
requestAds: (context: {
|
|
1382
|
+
requestAds: (context: ConversationContext | {
|
|
1366
1383
|
conversationContext: ConversationContext;
|
|
1367
1384
|
userContext?: UserContext;
|
|
1368
1385
|
}) => Promise<void>;
|
|
@@ -1374,7 +1391,7 @@ export declare interface UseAdSlotsResult {
|
|
|
1374
1391
|
*/
|
|
1375
1392
|
export declare interface UserContext {
|
|
1376
1393
|
userId?: string;
|
|
1377
|
-
sessionId
|
|
1394
|
+
sessionId?: string;
|
|
1378
1395
|
demographics?: UserDemographics;
|
|
1379
1396
|
profile?: UserProfile;
|
|
1380
1397
|
}
|