@action-x/ad-sdk 0.1.2 → 0.1.4
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 +5 -5
- package/dist/index.cjs +9 -9
- package/dist/index.d.ts +4 -1
- package/dist/index.js +210 -178
- package/dist/index.umd.js +10 -10
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ pnpm add @action-x/ad-sdk
|
|
|
17
17
|
**Import styles (required):**
|
|
18
18
|
|
|
19
19
|
```js
|
|
20
|
-
import '@action-x/ad-sdk/
|
|
20
|
+
import '@action-x/ad-sdk/style.css';
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -37,7 +37,7 @@ Below are AdCard wrapper examples for different frameworks. The component takes
|
|
|
37
37
|
<script setup>
|
|
38
38
|
import { ref, onMounted, onUnmounted } from 'vue';
|
|
39
39
|
import { AdManager } from '@action-x/ad-sdk';
|
|
40
|
-
import '@action-x/ad-sdk/
|
|
40
|
+
import '@action-x/ad-sdk/style.css';
|
|
41
41
|
|
|
42
42
|
const props = defineProps(['query', 'response']);
|
|
43
43
|
const adEl = ref(null);
|
|
@@ -64,7 +64,7 @@ onUnmounted(() => manager?.destroy());
|
|
|
64
64
|
// components/AdCard.tsx
|
|
65
65
|
import { useEffect, useRef } from 'react';
|
|
66
66
|
import { AdManager } from '@action-x/ad-sdk';
|
|
67
|
-
import '@action-x/ad-sdk/
|
|
67
|
+
import '@action-x/ad-sdk/style.css';
|
|
68
68
|
|
|
69
69
|
interface Props {
|
|
70
70
|
query: string;
|
|
@@ -99,7 +99,7 @@ export function AdCard({ query, response }: Props) {
|
|
|
99
99
|
|
|
100
100
|
```js
|
|
101
101
|
import { AdManager } from '@action-x/ad-sdk';
|
|
102
|
-
import '@action-x/ad-sdk/
|
|
102
|
+
import '@action-x/ad-sdk/style.css';
|
|
103
103
|
|
|
104
104
|
const manager = new AdManager({
|
|
105
105
|
apiBaseUrl: 'https://your-api.example.com/api/v1',
|
|
@@ -118,7 +118,7 @@ manager.render(document.getElementById('ad-card'));
|
|
|
118
118
|
### CDN (No Build Tool)
|
|
119
119
|
|
|
120
120
|
```html
|
|
121
|
-
<link rel="stylesheet" href="https://unpkg.com/@action-x/ad-sdk/
|
|
121
|
+
<link rel="stylesheet" href="https://unpkg.com/@action-x/ad-sdk/style.css" />
|
|
122
122
|
<script src="https://unpkg.com/@action-x/ad-sdk/dist/index.umd.js"></script>
|
|
123
123
|
|
|
124
124
|
<div id="ad-card"></div>
|
package/dist/index.cjs
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
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
|
|
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 b{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
|
+
/>`:"",d=s.price?`<span class="ax-ad-price">${s.price.display||s.price.value}</span>`:"",l=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>`:"",f=`
|
|
9
9
|
${c}
|
|
10
10
|
<div class="ax-ad-content">
|
|
11
11
|
${u}
|
|
12
12
|
<h3 class="ax-ad-title">${s.title}</h3>
|
|
13
13
|
${s.body?`<p class="ax-ad-body">${s.body}</p>`:""}
|
|
14
|
-
${
|
|
14
|
+
${l}
|
|
15
15
|
<div class="ax-ad-footer">
|
|
16
|
-
${
|
|
16
|
+
${d}
|
|
17
17
|
<a
|
|
18
18
|
href="${r.clickUrl}"
|
|
19
19
|
class="ax-ad-cta"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</a>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
|
-
`,
|
|
29
|
+
`,p=n?"</div>":"";return a+f+p}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=b.renderActionCard.bind(b)){return e.map(t).join(`
|
|
82
|
+
`)}}const K="ad_session_id";function R(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function E(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class C{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||K,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 N=new C,m=()=>N.getSessionId();class M{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(`${E(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/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"},d=R();d&&(c["X-API-Key"]=d,this.debug&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const l=await fetch(e,{method:"POST",headers:c,body:JSON.stringify(t),keepalive:!0,signal:r.signal});if(clearTimeout(a),!l.ok)throw new Error(`HTTP ${l.status}: ${l.statusText}`);const u=await l.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 P=new M,x=o=>P.trackImpression(o),A=o=>P.trackClick(o);function H(o,e){return`vt_${o}_${e}`}async function D(o){return Promise.all(o.map(e=>x(e)))}async function G(o){return Promise.all(o.map(e=>A(e)))}function F(o){const e=new M(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function j(o={}){const e=new C(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const V=class V{static renderActionCard(e,t,i={}){t.innerHTML=b.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=b.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:m(),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?`${n.requestId}:${n.slotId}:${e.original.id}:${e.tracking.viewToken||""}`:`no-analytics:${e.original.id}:${e.tracking.viewToken||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),d())},d=()=>{n?x({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:e.tracking.viewToken}).then(()=>{s&&s(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):s&&s(e)};if(typeof IntersectionObserver<"u"){let l=!1,u=null;const f=new IntersectionObserver(h=>{h.forEach(w=>{l=w.isIntersecting&&w.intersectionRatio>=.5,l?u||(u=setTimeout(()=>{u=null,l&&(c(),f.disconnect())},1e3)):u&&(clearTimeout(u),u=null)})},{threshold:.5}),p=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(p)}else c()}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 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;
|
|
139
|
+
`}};V.trackedImpressionKeys=new Set;let y=V;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(),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 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("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 T=()=>S.getInstance();function v(o){return T().collect(o)}function z(){return v().device}function O(){return v().user}function W(){return v().app}function Q(){return v().geo}function J(){return O().id}function X(){T().clearCache()}function Z(o){const e=v(o);return JSON.stringify(e,null,2)}function Y(){var t;const o=v();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 ee(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 B(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=T().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 te={width:400,height:200},I={variant:"horizontal",count:1,preferences:{}};function U(o={}){const e={variant:o.variant??I.variant,count:o.count??I.count,preferences:{...I.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:te,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 L,this.slots_config=U(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){const t="conversationContext"in e?e:{conversationContext:e},i=this.buildRequestKey(t),n=g.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const r=await B({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,sessionId:t.userContext.sessionId??m()}:{sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:r.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}render(e,t,i={}){const n=typeof e!="string",s=n?g.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const d=a.adIndex??0,l=c.ads[d];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,s),null;const u=this.adsAnalyticsMap.get(l.original.id),f=this.slots_config.find(w=>w.slotId===s),p=(f==null?void 0:f.format)||"action_card";r.innerHTML="";const h={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?y.renderSuffixAd(l,r,h):p==="source"?y.renderSponsoredSource(l,r,h):p==="lead_gen"?y.renderLeadGenAd(l,r,h):y.renderActionCard(l,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=U(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=m(),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=m(),s=await x({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")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let $=g;function _(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class q{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const c=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,n,s){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!r.enteredViewportAt&&(r.enteredViewportAt=d,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&r.enteredViewportAt){const u=d-r.enteredViewportAt;r.totalVisibleTimeMs+=u,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:d})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,n){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const l=c-a.enteredViewportAt;a.currentVisibleTimeMs=l,l>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var s,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const n=((s=this.config.batchConfig)==null?void 0:s.maxBatchSize)??5;if(i.length>=n)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=_();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=_();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let k=null;function ie(o,e){return k||(k=new q(o,e),k.setupBeforeUnload()),k}const ne="0.1.0";exports.AdManager=$;exports.AnalyticsSender=M;exports.ClientInfoCollector=S;exports.DOMRenderer=y;exports.HTMLRenderer=b;exports.SDK_VERSION=ne;exports.SessionManager=C;exports.ViewabilityTracker=q;exports.adaptAdToKoahAd=ee;exports.clearClientInfoCache=X;exports.createAnalytics=F;exports.createSession=j;exports.fetchAds=B;exports.generateViewToken=H;exports.getAppInfo=W;exports.getClientInfo=v;exports.getClientInfoCollector=T;exports.getClientInfoJSON=Z;exports.getClientInfoSummary=Y;exports.getDeviceInfo=z;exports.getGeoInfo=Q;exports.getSessionId=m;exports.getUserId=J;exports.getUserInfo=O;exports.getViewabilityTracker=ie;exports.trackAdClick=A;exports.trackAdImpression=x;exports.trackClicksBatch=G;exports.trackImpressionsBatch=D;
|
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ export declare class AdManager {
|
|
|
96
96
|
private isLoading;
|
|
97
97
|
private enabled;
|
|
98
98
|
private static readonly DEFAULT_SLOT_ID;
|
|
99
|
+
private static inFlightRequests;
|
|
99
100
|
private currentRequestId;
|
|
100
101
|
private adsAnalyticsMap;
|
|
101
102
|
constructor(config: AdManagerConfig);
|
|
@@ -111,6 +112,7 @@ export declare class AdManager {
|
|
|
111
112
|
conversationContext: ConversationContext;
|
|
112
113
|
userContext?: UserContext;
|
|
113
114
|
}): Promise<AdResponseBatch>;
|
|
115
|
+
private buildRequestKey;
|
|
114
116
|
/**
|
|
115
117
|
* Render a single ad from a slot into a container element
|
|
116
118
|
*
|
|
@@ -368,7 +370,7 @@ export declare interface AnalyticsSendConfig {
|
|
|
368
370
|
* Analytics 发送器类
|
|
369
371
|
*/
|
|
370
372
|
export declare class AnalyticsSender {
|
|
371
|
-
private
|
|
373
|
+
private explicitBaseUrl?;
|
|
372
374
|
private timeout;
|
|
373
375
|
private retryAttempts;
|
|
374
376
|
private retryDelay;
|
|
@@ -694,6 +696,7 @@ export declare interface DeviceInfo {
|
|
|
694
696
|
* DOM Renderer - Create DOM elements with event binding
|
|
695
697
|
*/
|
|
696
698
|
export declare class DOMRenderer {
|
|
699
|
+
private static trackedImpressionKeys;
|
|
697
700
|
/**
|
|
698
701
|
* Render Action Card ad into container
|
|
699
702
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class O {
|
|
2
2
|
constructor() {
|
|
3
3
|
this.events = {};
|
|
4
4
|
}
|
|
@@ -42,7 +42,7 @@ class P {
|
|
|
42
42
|
return ((t = this.events[e]) == null ? void 0 : t.length) || 0;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
class
|
|
45
|
+
class b {
|
|
46
46
|
/**
|
|
47
47
|
* Render Action Card ad as HTML string
|
|
48
48
|
*
|
|
@@ -57,17 +57,17 @@ class y {
|
|
|
57
57
|
alt="${s.title}"
|
|
58
58
|
class="ax-ad-image"
|
|
59
59
|
loading="lazy"
|
|
60
|
-
/>` : "",
|
|
60
|
+
/>` : "", d = s.price ? `<span class="ax-ad-price">${s.price.display || s.price.value}</span>` : "", l = s.rating ? `<div class="ax-ad-rating" aria-label="Rating: ${s.rating}">
|
|
61
61
|
${"★".repeat(Math.floor(s.rating))}${"☆".repeat(5 - Math.floor(s.rating))}
|
|
62
|
-
</div>` : "", u = s.brand ? `<span class="ax-ad-brand">${s.brand}</span>` : "",
|
|
62
|
+
</div>` : "", u = s.brand ? `<span class="ax-ad-brand">${s.brand}</span>` : "", f = `
|
|
63
63
|
${c}
|
|
64
64
|
<div class="ax-ad-content">
|
|
65
65
|
${u}
|
|
66
66
|
<h3 class="ax-ad-title">${s.title}</h3>
|
|
67
67
|
${s.body ? `<p class="ax-ad-body">${s.body}</p>` : ""}
|
|
68
|
-
${
|
|
68
|
+
${l}
|
|
69
69
|
<div class="ax-ad-footer">
|
|
70
|
-
${
|
|
70
|
+
${d}
|
|
71
71
|
<a
|
|
72
72
|
href="${r.clickUrl}"
|
|
73
73
|
class="ax-ad-cta"
|
|
@@ -80,8 +80,8 @@ class y {
|
|
|
80
80
|
</a>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
83
|
-
`,
|
|
84
|
-
return a +
|
|
83
|
+
`, p = n ? "</div>" : "";
|
|
84
|
+
return a + f + p;
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Render Suffix ad as HTML string
|
|
@@ -173,22 +173,31 @@ class y {
|
|
|
173
173
|
* @param renderFn - Render function to use
|
|
174
174
|
* @returns HTML string with all ads
|
|
175
175
|
*/
|
|
176
|
-
static renderAds(e, t =
|
|
176
|
+
static renderAds(e, t = b.renderActionCard.bind(b)) {
|
|
177
177
|
return e.map(t).join(`
|
|
178
178
|
`);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
const
|
|
182
|
-
function
|
|
181
|
+
const q = "ad_session_id";
|
|
182
|
+
function B() {
|
|
183
183
|
if (typeof window < "u") {
|
|
184
184
|
const o = window.__AD_CONFIG__;
|
|
185
185
|
if (o != null && o.apiKey)
|
|
186
186
|
return o.apiKey;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
-
|
|
189
|
+
function M(o) {
|
|
190
|
+
if (o) return o;
|
|
191
|
+
if (typeof window < "u") {
|
|
192
|
+
const e = window.__AD_CONFIG__;
|
|
193
|
+
if (e != null && e.apiBaseUrl)
|
|
194
|
+
return e.apiBaseUrl;
|
|
195
|
+
}
|
|
196
|
+
return "/api/v1";
|
|
197
|
+
}
|
|
198
|
+
class _ {
|
|
190
199
|
constructor(e = {}) {
|
|
191
|
-
this.memoryCache = /* @__PURE__ */ new Map(), this.sessionKey = e.sessionKey ||
|
|
200
|
+
this.memoryCache = /* @__PURE__ */ new Map(), this.sessionKey = e.sessionKey || q, this.storage = e.storage || "sessionStorage", (typeof window > "u" || typeof window.sessionStorage > "u") && (this.storage = "memory");
|
|
192
201
|
}
|
|
193
202
|
/**
|
|
194
203
|
* 获取 Session ID
|
|
@@ -222,17 +231,17 @@ class C {
|
|
|
222
231
|
this.storage === "sessionStorage" ? sessionStorage.removeItem(this.sessionKey) : this.memoryCache.delete(this.sessionKey);
|
|
223
232
|
}
|
|
224
233
|
}
|
|
225
|
-
const
|
|
226
|
-
class
|
|
234
|
+
const L = new _(), m = () => L.getSessionId();
|
|
235
|
+
class P {
|
|
227
236
|
constructor(e = {}) {
|
|
228
|
-
this.
|
|
237
|
+
this.explicitBaseUrl = e.baseUrl, this.timeout = e.timeout || 1e4, this.retryAttempts = e.retryAttempts ?? 2, this.retryDelay = e.retryDelay || 1e3, this.debug = e.debug || !1;
|
|
229
238
|
}
|
|
230
239
|
/**
|
|
231
240
|
* 发送广告展示事件
|
|
232
241
|
*/
|
|
233
242
|
async trackImpression(e) {
|
|
234
243
|
return this.sendRequest(
|
|
235
|
-
`${this.
|
|
244
|
+
`${M(this.explicitBaseUrl)}/ads/impression`,
|
|
236
245
|
e,
|
|
237
246
|
"impression"
|
|
238
247
|
);
|
|
@@ -242,7 +251,7 @@ class V {
|
|
|
242
251
|
*/
|
|
243
252
|
async trackClick(e) {
|
|
244
253
|
return this.sendRequest(
|
|
245
|
-
`${this.
|
|
254
|
+
`${M(this.explicitBaseUrl)}/ads/click`,
|
|
246
255
|
e,
|
|
247
256
|
"click"
|
|
248
257
|
);
|
|
@@ -258,18 +267,18 @@ class V {
|
|
|
258
267
|
this.debug && console.log(`[Analytics] Sending ${i} event (attempt ${s + 1}):`, t);
|
|
259
268
|
const r = new AbortController(), a = setTimeout(() => r.abort(), this.timeout), c = {
|
|
260
269
|
"Content-Type": "application/json"
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
const
|
|
270
|
+
}, d = B();
|
|
271
|
+
d && (c["X-API-Key"] = d, this.debug && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
|
|
272
|
+
const l = await fetch(e, {
|
|
264
273
|
method: "POST",
|
|
265
274
|
headers: c,
|
|
266
275
|
body: JSON.stringify(t),
|
|
267
276
|
keepalive: !0,
|
|
268
277
|
signal: r.signal
|
|
269
278
|
});
|
|
270
|
-
if (clearTimeout(a), !
|
|
271
|
-
throw new Error(`HTTP ${
|
|
272
|
-
const u = await
|
|
279
|
+
if (clearTimeout(a), !l.ok)
|
|
280
|
+
throw new Error(`HTTP ${l.status}: ${l.statusText}`);
|
|
281
|
+
const u = await l.json();
|
|
273
282
|
return this.debug && console.log(`[Analytics] ${i} event tracked successfully:`, u), u;
|
|
274
283
|
} catch (r) {
|
|
275
284
|
n = r, this.debug && console.warn(`[Analytics] ${i} tracking failed (attempt ${s + 1}):`, r), s < this.retryAttempts && await this.delay(this.retryDelay * (s + 1));
|
|
@@ -283,37 +292,37 @@ class V {
|
|
|
283
292
|
return new Promise((t) => setTimeout(t, e));
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
|
-
const
|
|
287
|
-
function
|
|
295
|
+
const U = new P(), A = (o) => U.trackImpression(o), S = (o) => U.trackClick(o);
|
|
296
|
+
function F(o, e) {
|
|
288
297
|
return `vt_${o}_${e}`;
|
|
289
298
|
}
|
|
290
|
-
async function D(o) {
|
|
291
|
-
return Promise.all(o.map((e) => x(e)));
|
|
292
|
-
}
|
|
293
299
|
async function G(o) {
|
|
294
300
|
return Promise.all(o.map((e) => A(e)));
|
|
295
301
|
}
|
|
296
|
-
function
|
|
297
|
-
|
|
302
|
+
async function D(o) {
|
|
303
|
+
return Promise.all(o.map((e) => S(e)));
|
|
304
|
+
}
|
|
305
|
+
function j(o) {
|
|
306
|
+
const e = new P(o);
|
|
298
307
|
return {
|
|
299
308
|
trackImpression: (t) => e.trackImpression(t),
|
|
300
309
|
trackClick: (t) => e.trackClick(t)
|
|
301
310
|
};
|
|
302
311
|
}
|
|
303
|
-
function
|
|
304
|
-
const e = new
|
|
312
|
+
function z(o = {}) {
|
|
313
|
+
const e = new _(o);
|
|
305
314
|
return {
|
|
306
315
|
getSessionId: () => e.getSessionId(),
|
|
307
316
|
regenerateSessionId: () => e.regenerateSessionId(),
|
|
308
317
|
clearSessionId: () => e.clearSessionId()
|
|
309
318
|
};
|
|
310
319
|
}
|
|
311
|
-
class
|
|
320
|
+
const I = class I {
|
|
312
321
|
/**
|
|
313
322
|
* Render Action Card ad into container
|
|
314
323
|
*/
|
|
315
324
|
static renderActionCard(e, t, i = {}) {
|
|
316
|
-
t.innerHTML =
|
|
325
|
+
t.innerHTML = b.renderActionCard(e, i);
|
|
317
326
|
const n = t.querySelector(".ax-ad-cta");
|
|
318
327
|
return n && n.addEventListener("click", (s) => {
|
|
319
328
|
s.preventDefault(), this.handleClick(e, i);
|
|
@@ -345,7 +354,7 @@ class v {
|
|
|
345
354
|
* Render Lead Gen ad into container
|
|
346
355
|
*/
|
|
347
356
|
static renderLeadGenAd(e, t, i = {}) {
|
|
348
|
-
t.innerHTML =
|
|
357
|
+
t.innerHTML = b.renderLeadGenAd(e);
|
|
349
358
|
const n = t.querySelector(".ax-ad-leadgen-cta");
|
|
350
359
|
return n && n.addEventListener("click", (s) => {
|
|
351
360
|
s.preventDefault(), this.handleClick(e, i);
|
|
@@ -368,12 +377,12 @@ class v {
|
|
|
368
377
|
*/
|
|
369
378
|
static handleClick(e, t) {
|
|
370
379
|
const { analytics: i, onClick: n } = t;
|
|
371
|
-
n && n(e), i &&
|
|
380
|
+
n && n(e), i && S({
|
|
372
381
|
requestId: i.requestId,
|
|
373
382
|
adId: e.original.id,
|
|
374
383
|
destinationUrl: e.tracking.clickUrl,
|
|
375
384
|
slotId: i.slotId,
|
|
376
|
-
sessionId:
|
|
385
|
+
sessionId: m(),
|
|
377
386
|
adTitle: e.adapted.title,
|
|
378
387
|
format: e.original.type
|
|
379
388
|
}).catch((s) => {
|
|
@@ -385,38 +394,45 @@ class v {
|
|
|
385
394
|
* Falls back to immediate tracking if IntersectionObserver is unavailable.
|
|
386
395
|
*/
|
|
387
396
|
static trackImpression(e, t, i) {
|
|
388
|
-
const { analytics: n, onImpression: s } = i, r =
|
|
389
|
-
|
|
397
|
+
const { analytics: n, onImpression: s } = i, r = n ? `${n.requestId}:${n.slotId}:${e.original.id}:${e.tracking.viewToken || ""}` : `no-analytics:${e.original.id}:${e.tracking.viewToken || ""}`;
|
|
398
|
+
if (this.trackedImpressionKeys.has(r))
|
|
399
|
+
return;
|
|
400
|
+
let a = !1;
|
|
401
|
+
const c = () => {
|
|
402
|
+
a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), d());
|
|
403
|
+
}, d = () => {
|
|
404
|
+
n ? A({
|
|
390
405
|
requestId: n.requestId,
|
|
391
406
|
adId: e.original.id,
|
|
392
407
|
slotId: n.slotId,
|
|
393
408
|
position: n.position,
|
|
394
409
|
totalAds: n.totalAds,
|
|
395
|
-
sessionId:
|
|
410
|
+
sessionId: m(),
|
|
396
411
|
adTitle: e.adapted.title,
|
|
397
412
|
format: e.original.type,
|
|
398
413
|
source: "internal",
|
|
399
414
|
viewToken: e.tracking.viewToken
|
|
400
415
|
}).then(() => {
|
|
401
416
|
s && s(e);
|
|
402
|
-
}).catch((
|
|
403
|
-
console.error("[DOMRenderer] Analytics impression tracking failed:",
|
|
417
|
+
}).catch((l) => {
|
|
418
|
+
console.error("[DOMRenderer] Analytics impression tracking failed:", l);
|
|
404
419
|
}) : s && s(e);
|
|
405
420
|
};
|
|
406
421
|
if (typeof IntersectionObserver < "u") {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
422
|
+
let l = !1, u = null;
|
|
423
|
+
const f = new IntersectionObserver(
|
|
424
|
+
(h) => {
|
|
425
|
+
h.forEach((w) => {
|
|
426
|
+
l = w.isIntersecting && w.intersectionRatio >= 0.5, l ? u || (u = setTimeout(() => {
|
|
427
|
+
u = null, l && (c(), f.disconnect());
|
|
428
|
+
}, 1e3)) : u && (clearTimeout(u), u = null);
|
|
413
429
|
});
|
|
414
430
|
},
|
|
415
431
|
{ threshold: 0.5 }
|
|
416
|
-
),
|
|
417
|
-
|
|
432
|
+
), p = t.querySelector(`[data-ad-id="${e.original.id}"]`) || t;
|
|
433
|
+
f.observe(p);
|
|
418
434
|
} else
|
|
419
|
-
|
|
435
|
+
c();
|
|
420
436
|
}
|
|
421
437
|
/**
|
|
422
438
|
* Generate HTML for Suffix Ad variants
|
|
@@ -490,8 +506,10 @@ class v {
|
|
|
490
506
|
</div>
|
|
491
507
|
`;
|
|
492
508
|
}
|
|
493
|
-
}
|
|
494
|
-
|
|
509
|
+
};
|
|
510
|
+
I.trackedImpressionKeys = /* @__PURE__ */ new Set();
|
|
511
|
+
let y = I;
|
|
512
|
+
class T {
|
|
495
513
|
constructor() {
|
|
496
514
|
this.cachedStaticInfo = null, this.cacheTimestamp = 0, this.CACHE_TTL = 36e5;
|
|
497
515
|
}
|
|
@@ -578,7 +596,7 @@ class S {
|
|
|
578
596
|
* Get singleton instance
|
|
579
597
|
*/
|
|
580
598
|
static getInstance() {
|
|
581
|
-
return this.instance || (this.instance = new
|
|
599
|
+
return this.instance || (this.instance = new T()), this.instance;
|
|
582
600
|
}
|
|
583
601
|
/**
|
|
584
602
|
* Collect all available client information
|
|
@@ -881,35 +899,35 @@ class S {
|
|
|
881
899
|
this.cachedStaticInfo = null, this.cacheTimestamp = 0;
|
|
882
900
|
}
|
|
883
901
|
}
|
|
884
|
-
const
|
|
885
|
-
function
|
|
886
|
-
return
|
|
887
|
-
}
|
|
888
|
-
function z() {
|
|
889
|
-
return g().device;
|
|
890
|
-
}
|
|
891
|
-
function q() {
|
|
892
|
-
return g().user;
|
|
902
|
+
const $ = () => T.getInstance();
|
|
903
|
+
function v(o) {
|
|
904
|
+
return $().collect(o);
|
|
893
905
|
}
|
|
894
906
|
function W() {
|
|
895
|
-
return
|
|
907
|
+
return v().device;
|
|
908
|
+
}
|
|
909
|
+
function K() {
|
|
910
|
+
return v().user;
|
|
896
911
|
}
|
|
897
912
|
function Q() {
|
|
898
|
-
return
|
|
913
|
+
return v().app;
|
|
899
914
|
}
|
|
900
915
|
function J() {
|
|
901
|
-
return
|
|
916
|
+
return v().geo;
|
|
902
917
|
}
|
|
903
918
|
function X() {
|
|
904
|
-
|
|
919
|
+
return K().id;
|
|
905
920
|
}
|
|
906
|
-
function Z(
|
|
907
|
-
|
|
921
|
+
function Z() {
|
|
922
|
+
$().clearCache();
|
|
923
|
+
}
|
|
924
|
+
function Y(o) {
|
|
925
|
+
const e = v(o);
|
|
908
926
|
return JSON.stringify(e, null, 2);
|
|
909
927
|
}
|
|
910
|
-
function
|
|
928
|
+
function ee() {
|
|
911
929
|
var t;
|
|
912
|
-
const o =
|
|
930
|
+
const o = v();
|
|
913
931
|
return [
|
|
914
932
|
o.device.os,
|
|
915
933
|
o.device.osv,
|
|
@@ -918,7 +936,7 @@ function Y() {
|
|
|
918
936
|
((t = o.geo) == null ? void 0 : t.country) || "Unknown"
|
|
919
937
|
].join(" / ");
|
|
920
938
|
}
|
|
921
|
-
function
|
|
939
|
+
function te(o) {
|
|
922
940
|
var e, t;
|
|
923
941
|
return {
|
|
924
942
|
id: o.original.id,
|
|
@@ -945,8 +963,8 @@ function ee(o) {
|
|
|
945
963
|
}
|
|
946
964
|
};
|
|
947
965
|
}
|
|
948
|
-
async function
|
|
949
|
-
const t = e.apiBaseUrl || "/api/v1", n =
|
|
966
|
+
async function R(o, e = {}) {
|
|
967
|
+
const t = e.apiBaseUrl || "/api/v1", n = $().collect(), s = {
|
|
950
968
|
...o,
|
|
951
969
|
clientInfo: n
|
|
952
970
|
// Auto-injected
|
|
@@ -966,16 +984,16 @@ async function K(o, e = {}) {
|
|
|
966
984
|
throw new Error(c.error || "Ad request failed");
|
|
967
985
|
return c.data;
|
|
968
986
|
}
|
|
969
|
-
const
|
|
987
|
+
const N = { width: 400, height: 200 }, x = {
|
|
970
988
|
variant: "horizontal",
|
|
971
989
|
count: 1,
|
|
972
990
|
preferences: {}
|
|
973
991
|
};
|
|
974
|
-
function
|
|
992
|
+
function C(o = {}) {
|
|
975
993
|
const e = {
|
|
976
|
-
variant: o.variant ??
|
|
977
|
-
count: o.count ??
|
|
978
|
-
preferences: { ...
|
|
994
|
+
variant: o.variant ?? x.variant,
|
|
995
|
+
count: o.count ?? x.count,
|
|
996
|
+
preferences: { ...x.preferences, ...o.preferences }
|
|
979
997
|
};
|
|
980
998
|
return [
|
|
981
999
|
{
|
|
@@ -983,16 +1001,16 @@ function $(o = {}) {
|
|
|
983
1001
|
slotName: "Action Card",
|
|
984
1002
|
format: "action_card",
|
|
985
1003
|
variant: e.variant,
|
|
986
|
-
size:
|
|
1004
|
+
size: N,
|
|
987
1005
|
count: e.count,
|
|
988
1006
|
preferences: e.preferences,
|
|
989
1007
|
placement: { position: "below_fold", context: "post_response" }
|
|
990
1008
|
}
|
|
991
1009
|
];
|
|
992
1010
|
}
|
|
993
|
-
const
|
|
1011
|
+
const g = class g {
|
|
994
1012
|
constructor(e) {
|
|
995
|
-
this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new
|
|
1013
|
+
this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new O(), this.slots_config = C(e.cardOption), typeof window < "u" && (window.__AD_CONFIG__ = {
|
|
996
1014
|
...window.__AD_CONFIG__ || {},
|
|
997
1015
|
apiKey: e.apiKey,
|
|
998
1016
|
apiBaseUrl: e.apiBaseUrl
|
|
@@ -1003,60 +1021,74 @@ const w = class w {
|
|
|
1003
1021
|
});
|
|
1004
1022
|
}
|
|
1005
1023
|
async requestAds(e) {
|
|
1024
|
+
const t = "conversationContext" in e ? e : { conversationContext: e }, i = this.buildRequestKey(t), n = g.inFlightRequests.get(i);
|
|
1025
|
+
if (n)
|
|
1026
|
+
return this.config.debug && console.log("[AdManager] Reusing in-flight request for identical context"), n;
|
|
1006
1027
|
if (!this.enabled)
|
|
1007
1028
|
throw this.config.debug && console.warn("[AdManager] Ads are disabled, skipping request"), new Error("Ads are disabled");
|
|
1008
1029
|
if (this.isLoading)
|
|
1009
1030
|
throw this.config.debug && console.warn("[AdManager] Request already in progress"), new Error("Request already in progress");
|
|
1010
1031
|
this.isLoading = !0, this.eventBus.emit("adsLoading"), this.config.debug && console.log("[AdManager] Starting ad request...");
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
+
const s = (async () => {
|
|
1033
|
+
try {
|
|
1034
|
+
const r = await R(
|
|
1035
|
+
{
|
|
1036
|
+
conversationContext: t.conversationContext,
|
|
1037
|
+
userContext: t.userContext ? { ...t.userContext, sessionId: t.userContext.sessionId ?? m() } : { sessionId: m() },
|
|
1038
|
+
slots: this.slots_config
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
1042
|
+
apiKey: this.config.apiKey
|
|
1043
|
+
}
|
|
1044
|
+
);
|
|
1045
|
+
this.currentRequestId = r.requestId, this.adsAnalyticsMap.clear(), r.slots.forEach((c) => {
|
|
1046
|
+
c.ads.forEach((d, l) => {
|
|
1047
|
+
const u = d.original.id;
|
|
1048
|
+
this.adsAnalyticsMap.set(u, {
|
|
1049
|
+
requestId: r.requestId,
|
|
1050
|
+
slotId: c.slotId,
|
|
1051
|
+
position: l,
|
|
1052
|
+
totalAds: c.ads.length
|
|
1053
|
+
});
|
|
1032
1054
|
});
|
|
1033
1055
|
});
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
})
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1056
|
+
const a = {};
|
|
1057
|
+
return r.slots.forEach((c) => {
|
|
1058
|
+
a[c.slotId] = c;
|
|
1059
|
+
}), this.slots = a, this.eventBus.emit("adsUpdated", this.slots), this.config.debug && console.log("[AdManager] Ads received:", {
|
|
1060
|
+
slotCount: Object.keys(this.slots).length,
|
|
1061
|
+
slots: Object.keys(this.slots)
|
|
1062
|
+
}), r;
|
|
1063
|
+
} catch (r) {
|
|
1064
|
+
const a = r;
|
|
1065
|
+
throw this.eventBus.emit("adsError", a), a;
|
|
1066
|
+
} finally {
|
|
1067
|
+
this.isLoading = !1, g.inFlightRequests.delete(i);
|
|
1068
|
+
}
|
|
1069
|
+
})();
|
|
1070
|
+
return g.inFlightRequests.set(i, s), s;
|
|
1071
|
+
}
|
|
1072
|
+
buildRequestKey(e) {
|
|
1073
|
+
return JSON.stringify({
|
|
1074
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
1075
|
+
apiKey: this.config.apiKey,
|
|
1076
|
+
conversationContext: e.conversationContext,
|
|
1077
|
+
userContext: e.userContext || null,
|
|
1078
|
+
slots: this.slots_config
|
|
1079
|
+
});
|
|
1048
1080
|
}
|
|
1049
1081
|
render(e, t, i = {}) {
|
|
1050
|
-
const n = typeof e != "string", s = n ?
|
|
1082
|
+
const n = typeof e != "string", s = n ? g.DEFAULT_SLOT_ID : e, r = n ? e : t, a = (n ? t : i) || {};
|
|
1051
1083
|
if (!r)
|
|
1052
1084
|
return this.config.debug && console.warn("[AdManager] Render container is required"), null;
|
|
1053
1085
|
const c = this.slots[s];
|
|
1054
1086
|
if (!c || !c.ads || c.ads.length === 0)
|
|
1055
1087
|
return this.config.debug && console.warn("[AdManager] No ads in slot:", s), null;
|
|
1056
|
-
const
|
|
1057
|
-
if (!
|
|
1058
|
-
return this.config.debug && console.warn(`[AdManager] Ad at index ${
|
|
1059
|
-
const u = this.adsAnalyticsMap.get(
|
|
1088
|
+
const d = a.adIndex ?? 0, l = c.ads[d];
|
|
1089
|
+
if (!l)
|
|
1090
|
+
return this.config.debug && console.warn(`[AdManager] Ad at index ${d} not found in slot:`, s), null;
|
|
1091
|
+
const u = this.adsAnalyticsMap.get(l.original.id), f = this.slots_config.find((w) => w.slotId === s), p = (f == null ? void 0 : f.format) || "action_card";
|
|
1060
1092
|
r.innerHTML = "";
|
|
1061
1093
|
const h = {
|
|
1062
1094
|
analytics: u,
|
|
@@ -1064,7 +1096,7 @@ const w = class w {
|
|
|
1064
1096
|
onClick: a.onClick,
|
|
1065
1097
|
onImpression: a.onImpression
|
|
1066
1098
|
};
|
|
1067
|
-
return
|
|
1099
|
+
return p === "suffix" ? y.renderSuffixAd(l, r, h) : p === "source" ? y.renderSponsoredSource(l, r, h) : p === "lead_gen" ? y.renderLeadGenAd(l, r, h) : y.renderActionCard(l, r, h);
|
|
1068
1100
|
}
|
|
1069
1101
|
/**
|
|
1070
1102
|
* Get ad slots data
|
|
@@ -1103,7 +1135,7 @@ const w = class w {
|
|
|
1103
1135
|
* Update configuration
|
|
1104
1136
|
*/
|
|
1105
1137
|
updateConfig(e) {
|
|
1106
|
-
this.config = { ...this.config, ...e }, e.cardOption !== void 0 && (this.slots_config =
|
|
1138
|
+
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);
|
|
1107
1139
|
}
|
|
1108
1140
|
/**
|
|
1109
1141
|
* Clear all slot data
|
|
@@ -1158,7 +1190,7 @@ const w = class w {
|
|
|
1158
1190
|
const n = this.adsAnalyticsMap.get(e);
|
|
1159
1191
|
if (!n || !this.currentRequestId)
|
|
1160
1192
|
return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
|
|
1161
|
-
const s =
|
|
1193
|
+
const s = m(), r = await S({
|
|
1162
1194
|
requestId: this.currentRequestId,
|
|
1163
1195
|
adId: e,
|
|
1164
1196
|
destinationUrl: t,
|
|
@@ -1177,7 +1209,7 @@ const w = class w {
|
|
|
1177
1209
|
const i = this.adsAnalyticsMap.get(e);
|
|
1178
1210
|
if (!i || !this.currentRequestId)
|
|
1179
1211
|
return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
|
|
1180
|
-
const n =
|
|
1212
|
+
const n = m(), s = await A({
|
|
1181
1213
|
requestId: this.currentRequestId,
|
|
1182
1214
|
adId: e,
|
|
1183
1215
|
slotId: i.slotId,
|
|
@@ -1198,16 +1230,16 @@ const w = class w {
|
|
|
1198
1230
|
this.removeAllListeners(), this.clearSlots(), this.config.debug && console.log("[AdManager] Destroyed");
|
|
1199
1231
|
}
|
|
1200
1232
|
};
|
|
1201
|
-
|
|
1202
|
-
let
|
|
1203
|
-
function
|
|
1233
|
+
g.DEFAULT_SLOT_ID = "action_card", g.inFlightRequests = /* @__PURE__ */ new Map();
|
|
1234
|
+
let V = g;
|
|
1235
|
+
function E() {
|
|
1204
1236
|
if (typeof window < "u") {
|
|
1205
1237
|
const o = window.__AD_CONFIG__;
|
|
1206
1238
|
if (o != null && o.apiKey)
|
|
1207
1239
|
return o.apiKey;
|
|
1208
1240
|
}
|
|
1209
1241
|
}
|
|
1210
|
-
class
|
|
1242
|
+
class H {
|
|
1211
1243
|
constructor(e = {}, t = "/api/v1") {
|
|
1212
1244
|
var i, n;
|
|
1213
1245
|
this.observers = /* @__PURE__ */ new Map(), this.metrics = /* @__PURE__ */ new Map(), this.timers = /* @__PURE__ */ new Map(), this.batchTimers = /* @__PURE__ */ new Map(), this.eventQueue = /* @__PURE__ */ new Map(), this.isTracking = /* @__PURE__ */ new Map(), this.config = {
|
|
@@ -1242,7 +1274,7 @@ class N {
|
|
|
1242
1274
|
};
|
|
1243
1275
|
this.metrics.set(e, r), this.isTracking.set(e, !0), this.eventQueue.set(e, []);
|
|
1244
1276
|
const a = new IntersectionObserver(
|
|
1245
|
-
(
|
|
1277
|
+
(d) => this.handleIntersection(e, d[0], i, n, s),
|
|
1246
1278
|
{
|
|
1247
1279
|
threshold: this.createThresholds()
|
|
1248
1280
|
}
|
|
@@ -1274,8 +1306,8 @@ class N {
|
|
|
1274
1306
|
if (!r || !this.isTracking.get(e)) return;
|
|
1275
1307
|
const a = r.isViewable, c = Math.round(t.intersectionRatio * 100);
|
|
1276
1308
|
r.visiblePercentage = c, r.maxVisiblePercentage = Math.max(r.maxVisiblePercentage, c);
|
|
1277
|
-
const
|
|
1278
|
-
if (
|
|
1309
|
+
const d = Date.now(), l = c >= this.config.minVisiblePercentage;
|
|
1310
|
+
if (l && !r.enteredViewportAt && (r.enteredViewportAt = d, r.enterCount++, this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`), this.queueEvent(e, {
|
|
1279
1311
|
adId: e,
|
|
1280
1312
|
sessionId: i,
|
|
1281
1313
|
requestId: n,
|
|
@@ -1285,9 +1317,9 @@ class N {
|
|
|
1285
1317
|
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1286
1318
|
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1287
1319
|
isViewable: !1,
|
|
1288
|
-
timestamp:
|
|
1289
|
-
})), !
|
|
1290
|
-
const u =
|
|
1320
|
+
timestamp: d
|
|
1321
|
+
})), !l && r.enteredViewportAt) {
|
|
1322
|
+
const u = d - r.enteredViewportAt;
|
|
1291
1323
|
r.totalVisibleTimeMs += u, r.enteredViewportAt = null, r.currentVisibleTimeMs = 0, this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`), this.queueEvent(e, {
|
|
1292
1324
|
adId: e,
|
|
1293
1325
|
sessionId: i,
|
|
@@ -1298,7 +1330,7 @@ class N {
|
|
|
1298
1330
|
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1299
1331
|
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1300
1332
|
isViewable: r.isViewable,
|
|
1301
|
-
timestamp:
|
|
1333
|
+
timestamp: d
|
|
1302
1334
|
});
|
|
1303
1335
|
}
|
|
1304
1336
|
r.isViewable !== a && r.isViewable && this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`), this.metrics.set(e, r);
|
|
@@ -1314,10 +1346,10 @@ class N {
|
|
|
1314
1346
|
clearInterval(r);
|
|
1315
1347
|
return;
|
|
1316
1348
|
}
|
|
1317
|
-
const c = Date.now(),
|
|
1349
|
+
const c = Date.now(), d = a.isViewable;
|
|
1318
1350
|
if (a.enteredViewportAt) {
|
|
1319
|
-
const
|
|
1320
|
-
a.currentVisibleTimeMs =
|
|
1351
|
+
const l = c - a.enteredViewportAt;
|
|
1352
|
+
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, {
|
|
1321
1353
|
adId: e,
|
|
1322
1354
|
sessionId: t,
|
|
1323
1355
|
requestId: i,
|
|
@@ -1351,10 +1383,10 @@ class N {
|
|
|
1351
1383
|
else {
|
|
1352
1384
|
const a = this.batchTimers.get(e);
|
|
1353
1385
|
a && clearTimeout(a);
|
|
1354
|
-
const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4,
|
|
1386
|
+
const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4, d = setTimeout(() => {
|
|
1355
1387
|
this.flushQueue(e);
|
|
1356
1388
|
}, c);
|
|
1357
|
-
this.batchTimers.set(e,
|
|
1389
|
+
this.batchTimers.set(e, d);
|
|
1358
1390
|
}
|
|
1359
1391
|
}
|
|
1360
1392
|
/**
|
|
@@ -1368,7 +1400,7 @@ class N {
|
|
|
1368
1400
|
try {
|
|
1369
1401
|
const n = {
|
|
1370
1402
|
"Content-Type": "application/json"
|
|
1371
|
-
}, s =
|
|
1403
|
+
}, s = E();
|
|
1372
1404
|
s && (n["x-api-key"] = s), await fetch(`${this.baseUrl}/ads/viewability/batch`, {
|
|
1373
1405
|
method: "POST",
|
|
1374
1406
|
headers: n,
|
|
@@ -1442,7 +1474,7 @@ class N {
|
|
|
1442
1474
|
if (t && t.length > 0) {
|
|
1443
1475
|
const i = {
|
|
1444
1476
|
"Content-Type": "application/json"
|
|
1445
|
-
}, n =
|
|
1477
|
+
}, n = E();
|
|
1446
1478
|
n && (i["x-api-key"] = n), fetch(`${this.baseUrl}/ads/viewability/batch`, {
|
|
1447
1479
|
method: "POST",
|
|
1448
1480
|
headers: i,
|
|
@@ -1454,39 +1486,39 @@ class N {
|
|
|
1454
1486
|
});
|
|
1455
1487
|
}
|
|
1456
1488
|
}
|
|
1457
|
-
let
|
|
1458
|
-
function
|
|
1459
|
-
return
|
|
1489
|
+
let k = null;
|
|
1490
|
+
function ie(o, e) {
|
|
1491
|
+
return k || (k = new H(o, e), k.setupBeforeUnload()), k;
|
|
1460
1492
|
}
|
|
1461
|
-
const
|
|
1493
|
+
const ne = "0.1.0";
|
|
1462
1494
|
export {
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1495
|
+
V as AdManager,
|
|
1496
|
+
P as AnalyticsSender,
|
|
1497
|
+
T as ClientInfoCollector,
|
|
1498
|
+
y as DOMRenderer,
|
|
1499
|
+
b as HTMLRenderer,
|
|
1500
|
+
ne as SDK_VERSION,
|
|
1501
|
+
_ as SessionManager,
|
|
1502
|
+
H as ViewabilityTracker,
|
|
1503
|
+
te as adaptAdToKoahAd,
|
|
1504
|
+
Z as clearClientInfoCache,
|
|
1505
|
+
j as createAnalytics,
|
|
1506
|
+
z as createSession,
|
|
1507
|
+
R as fetchAds,
|
|
1508
|
+
F as generateViewToken,
|
|
1509
|
+
Q as getAppInfo,
|
|
1510
|
+
v as getClientInfo,
|
|
1511
|
+
$ as getClientInfoCollector,
|
|
1512
|
+
Y as getClientInfoJSON,
|
|
1513
|
+
ee as getClientInfoSummary,
|
|
1514
|
+
W as getDeviceInfo,
|
|
1515
|
+
J as getGeoInfo,
|
|
1516
|
+
m as getSessionId,
|
|
1517
|
+
X as getUserId,
|
|
1518
|
+
K as getUserInfo,
|
|
1519
|
+
ie as getViewabilityTracker,
|
|
1520
|
+
S as trackAdClick,
|
|
1521
|
+
A as trackAdImpression,
|
|
1522
|
+
D as trackClicksBatch,
|
|
1523
|
+
G as trackImpressionsBatch
|
|
1492
1524
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(l,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(l=typeof globalThis<"u"?globalThis:l||self,k(l.ActionXAdSDK={}))})(this,function(l){"use strict";class k{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(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 w{static renderActionCard(e,t={}){var f;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=(f=s.image)!=null&&f.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
|
+
/>`:"",u=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>`:"",h=s.brand?`<span class="ax-ad-brand">${s.brand}</span>`:"",
|
|
9
|
-
${
|
|
8
|
+
</div>`:"",h=s.brand?`<span class="ax-ad-brand">${s.brand}</span>`:"",p=`
|
|
9
|
+
${c}
|
|
10
10
|
<div class="ax-ad-content">
|
|
11
11
|
${h}
|
|
12
12
|
<h3 class="ax-ad-title">${s.title}</h3>
|
|
13
13
|
${s.body?`<p class="ax-ad-body">${s.body}</p>`:""}
|
|
14
|
-
${
|
|
14
|
+
${d}
|
|
15
15
|
<div class="ax-ad-footer">
|
|
16
|
-
${
|
|
16
|
+
${u}
|
|
17
17
|
<a
|
|
18
18
|
href="${r.clickUrl}"
|
|
19
19
|
class="ax-ad-cta"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</a>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
|
-
`,
|
|
29
|
+
`,b=n?"</div>":"";return a+p+b}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=w.renderActionCard.bind(w)){return e.map(t).join(`
|
|
82
|
+
`)}}const R="ad_session_id";function N(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function _(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class C{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||R,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 H=new C,m=()=>H.getSessionId();class M{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let 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"},u=N();u&&(c["X-API-Key"]=u,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 h=await d.json();return this.debug&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}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 P=new M,A=o=>P.trackImpression(o),x=o=>P.trackClick(o);function D(o,e){return`vt_${o}_${e}`}async function G(o){return Promise.all(o.map(e=>A(e)))}async function F(o){return Promise.all(o.map(e=>x(e)))}function j(o){const e=new M(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function z(o={}){const e=new C(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const U=class U{static renderActionCard(e,t,i={}){t.innerHTML=w.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=w.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&&x({requestId:i.requestId,adId:e.original.id,destinationUrl:e.tracking.clickUrl,slotId:i.slotId,sessionId:m(),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?`${n.requestId}:${n.slotId}:${e.original.id}:${e.tracking.viewToken||""}`:`no-analytics:${e.original.id}:${e.tracking.viewToken||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),u())},u=()=>{n?A({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:e.tracking.viewToken}).then(()=>{s&&s(e)}).catch(d=>{console.error("[DOMRenderer] Analytics impression tracking failed:",d)}):s&&s(e)};if(typeof IntersectionObserver<"u"){let d=!1,h=null;const p=new IntersectionObserver(f=>{f.forEach($=>{d=$.isIntersecting&&$.intersectionRatio>=.5,d?h||(h=setTimeout(()=>{h=null,d&&(c(),p.disconnect())},1e3)):h&&(clearTimeout(h),h=null)})},{threshold:.5}),b=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;p.observe(b)}else c()}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 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 l=await a.json();if(!l.success)throw new Error(l.error||"Ad request failed");return l.data}const ee={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function U(o={}){const e={variant:o.variant??C.variant,count:o.count??C.count,preferences:{...C.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:ee,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const I=class I{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 y,this.slots_config=U(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 l=r.original.id;this.adsAnalyticsMap.set(l,{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?I.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 l=this.slots[s];if(!l||!l.ads||l.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const d=a.adIndex??0,u=l.ads[d];if(!u)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(u.original.id),v=this.slots_config.find(ne=>ne.slotId===s),w=(v==null?void 0:v.format)||"action_card";r.innerHTML="";const p={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return w==="suffix"?b.renderSuffixAd(u,r,p):w==="source"?b.renderSponsoredSource(u,r,p):w==="lead_gen"?b.renderLeadGenAd(u,r,p):b.renderActionCard(u,r,p)}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=U(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")}};I.DEFAULT_SLOT_ID="action_card";let V=I;function O(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class L{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const l=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,l)}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,l=Math.round(t.intersectionRatio*100);r.visiblePercentage=l,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,l);const d=Date.now(),u=l>=this.config.minVisiblePercentage;if(u&&!r.enteredViewportAt&&(r.enteredViewportAt=d,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:l,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!u&&r.enteredViewportAt){const h=d-r.enteredViewportAt;r.totalVisibleTimeMs+=h,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:l,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:d})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,n){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const l=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const u=l-a.enteredViewportAt;a.currentVisibleTimeMs=u,u>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:l})))}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 l=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},l);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=O();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=O();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let T=null;function te(o,e){return T||(T=new L(o,e),T.setupBeforeUnload()),T}const ie="0.1.0";c.AdManager=V,c.AnalyticsSender=M,c.ClientInfoCollector=x,c.DOMRenderer=b,c.HTMLRenderer=m,c.SDK_VERSION=ie,c.SessionManager=$,c.ViewabilityTracker=L,c.adaptAdToKoahAd=Y,c.clearClientInfoCache=J,c.createAnalytics=G,c.createSession=j,c.fetchAds=P,c.generateViewToken=N,c.getAppInfo=z,c.getClientInfo=g,c.getClientInfoCollector=S,c.getClientInfoJSON=X,c.getClientInfoSummary=Z,c.getDeviceInfo=F,c.getGeoInfo=W,c.getSessionId=f,c.getUserId=Q,c.getUserInfo=_,c.getViewabilityTracker=te,c.trackAdClick=A,c.trackAdImpression=k,c.trackClicksBatch=D,c.trackImpressionsBatch=H,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
|
|
139
|
+
`}};U.trackedImpressionKeys=new Set;let y=U;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(),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 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("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 T=()=>S.getInstance();function v(o){return T().collect(o)}function W(){return v().device}function O(){return v().user}function Q(){return v().app}function J(){return v().geo}function X(){return O().id}function Z(){T().clearCache()}function Y(o){const e=v(o);return JSON.stringify(e,null,2)}function ee(){var t;const o=v();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 te(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 B(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=T().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 ie={width:400,height:200},V={variant:"horizontal",count:1,preferences:{}};function q(o={}){const e={variant:o.variant??V.variant,count:o.count??V.count,preferences:{...V.preferences,...o.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 g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new k,this.slots_config=q(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){const t="conversationContext"in e?e:{conversationContext:e},i=this.buildRequestKey(t),n=g.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const r=await B({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,sessionId:t.userContext.sessionId??m()}:{sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((u,d)=>{const h=u.original.id;this.adsAnalyticsMap.set(h,{requestId:r.requestId,slotId:c.slotId,position:d,totalAds:c.ads.length})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}render(e,t,i={}){const n=typeof e!="string",s=n?g.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const u=a.adIndex??0,d=c.ads[u];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(d.original.id),p=this.slots_config.find($=>$.slotId===s),b=(p==null?void 0:p.format)||"action_card";r.innerHTML="";const f={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return b==="suffix"?y.renderSuffixAd(d,r,f):b==="source"?y.renderSponsoredSource(d,r,f):b==="lead_gen"?y.renderLeadGenAd(d,r,f):y.renderActionCard(d,r,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=q(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=m(),r=await x({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=m(),s=await A({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")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let E=g;function L(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class K{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(u=>this.handleIntersection(e,u[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 u=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!r.enteredViewportAt&&(r.enteredViewportAt=u,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${u}`),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:u})),!d&&r.enteredViewportAt){const h=u-r.enteredViewportAt;r.totalVisibleTimeMs+=h,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:u})}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(),u=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,u||(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,u=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,u)}}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=L();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=L();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let I=null;function ne(o,e){return I||(I=new K(o,e),I.setupBeforeUnload()),I}const se="0.1.0";l.AdManager=E,l.AnalyticsSender=M,l.ClientInfoCollector=S,l.DOMRenderer=y,l.HTMLRenderer=w,l.SDK_VERSION=se,l.SessionManager=C,l.ViewabilityTracker=K,l.adaptAdToKoahAd=te,l.clearClientInfoCache=Z,l.createAnalytics=j,l.createSession=z,l.fetchAds=B,l.generateViewToken=D,l.getAppInfo=Q,l.getClientInfo=v,l.getClientInfoCollector=T,l.getClientInfoJSON=Y,l.getClientInfoSummary=ee,l.getDeviceInfo=W,l.getGeoInfo=J,l.getSessionId=m,l.getUserId=X,l.getUserInfo=O,l.getViewabilityTracker=ne,l.trackAdClick=x,l.trackAdImpression=A,l.trackClicksBatch=F,l.trackImpressionsBatch=G,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@action-x/ad-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Zero-dependency ad SDK for ActionX",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc --noEmit && vite build",
|
|
22
22
|
"dev": "vite build --watch",
|
|
23
|
-
"typecheck": "tsc --noEmit"
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"release": "npm version patch && git push && git push --tags",
|
|
25
|
+
"release:minor": "npm version minor && git push && git push --tags",
|
|
26
|
+
"release:major": "npm version major && git push && git push --tags"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
26
29
|
"typescript": "^5.3.0",
|