@cnv-vn/track 0.2.0-beta.6 → 0.2.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cnv-track.esm.js +6 -0
- package/dist/cnv-track.min.js +6 -0
- package/dist/cnv-track.umd.cjs +6 -0
- package/dist/types/index.d.ts +800 -0
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @cnv-vn/track v0.2.0-beta.7
|
|
3
|
+
* (c) 2026 CNV
|
|
4
|
+
* Released under the UNLICENSED license.
|
|
5
|
+
*/
|
|
6
|
+
const t=["pdf","doc","docx","xls","xlsx","ppt","pptx","txt","csv","rtf","zip","rar","7z","tar","gz","mp3","mp4","mov","wav","avi","mkv","dmg","exe","pkg","apk","msi","svg"];function n(t){return!!t&&"object"==typeof t&&1===t.nodeType&&"A"===t.tagName}function e(t){const n=t.lastIndexOf("/");return-1===n?t:t.slice(n+1)}function r(t){var n,e,r;let o=(null!==(n=t.textContent)&&void 0!==n?n:"").trim();return o||(o=(null!==(e=t.getAttribute("aria-label"))&&void 0!==e?e:"").trim()),o||(o=(null!==(r=t.getAttribute("title"))&&void 0!==r?r:"").trim()),o.length>200?o.slice(0,200):o}const o=200;function i(t){let n="";try{n=(t.textContent||"").replace(/\s+/g," ").trim(),n||(n=(t.getAttribute("aria-label")||"").trim()),n||(n=(t.getAttribute("title")||"").trim()),n=n.normalize("NFC")}catch(t){return""}return n.length>o?n.slice(0,o):n}function c(t){const n=!1!==t.caseInsensitive;if("regex"===t.mode)return{mode:"regex",needle:t.value,ci:n,re:m(t.value,n)};const e=t.value.normalize("NFC");return{mode:t.mode,needle:n?e.toLowerCase():e,ci:n,re:null}}function u(t,n){try{return t.matches(n)}catch(t){return!1}}function l(t){try{if("A"===t.tagName){const n=t.getAttribute("href");return n?p(n):null}const n=t.getAttribute("formaction");if(n)return p(n);const e=t.getAttribute("data-href");if(e)return p(e);const r=t.closest("form");if(r){const t=r.getAttribute("action");if(t)return p(t)}return null}catch(t){return null}}function s(t){return"regex"===t.mode?{mode:"regex",needle:t.value,re:m(t.value,!0)}:{mode:t.mode,needle:t.value.toLowerCase(),re:null}}function a(t){if("startsWith"===t.mode)return{mode:"startsWith",needle:t.value,re:null};const n="glob"===t.mode?"^"+t.value.replace(/[.+^${}()|[\]\\?]/g,"\\$&").replace(/\*/g,".*")+"$":t.value;return{mode:t.mode,needle:t.value,re:m(n,!1)}}function f(t,n){return"startsWith"===t.mode?0===n.indexOf(t.needle):!t.re||b(t.re,n)}const d=/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,h=/\+?\d[\d\s().-]{7,}\d/g;function y(t){if(!t)return t;try{return t.replace(d,"[email]").replace(h,"[phone]")}catch(n){return t}}function v(t){return!!t&&"object"==typeof t&&1===t.nodeType}function p(t){try{return new URL(t,location.href).href}catch(n){return t}}function m(t,n){try{return!t||t.length>1e3?null:new RegExp(t,n?"i":"")}catch(t){return null}}function b(t,n){try{return t.test(n)}catch(t){return!1}}function w(t,n){if("undefined"==typeof document)return()=>{};const e=(t=>{const n=t.globalThrottle||{},e=[],r=t.rules||[];for(let t=0;t<r.length;t++){const o=r[t];if(!o||!1===o.enabled)continue;if(!o.match||!o.event)continue;const i=o.match,u=o.throttle||{};e.push({rule:o,elementSpec:g(o),selector:i.selector||null,text:i.text?c(i.text):null,dest:i.destinationUrl?s(i.destinationUrl):null,urlScope:o.urlScope?a(o.urlScope):null,dedupeWindowMs:S(u.dedupeWindowMs,S(n.dedupeWindowMs,500)),maxPerPageview:S(u.maxPerPageview,S(n.maxPerPageview,1/0)),samplingRate:A(S(u.samplingRate,S(n.samplingRate,1)))})}return e.sort((t,n)=>(t.rule.priority||0)-(n.rule.priority||0)),e})(n);if(0===e.length)return()=>{};const r=new WeakMap;let o=0;const i=t=>{let n=r.get(t);return void 0===n&&(n=++o,r.set(t,n)),String(n)},u=new Map,l=new Map;let d=O();function h(n){try{if(0!==n.button)return;const r=O();r!==d&&(d=r,l.clear(),u.clear());const o=(t=>{const n=[];try{const e="function"==typeof t.composedPath?t.composedPath():[];if(e&&e.length>0){for(const t of e)v(t)&&n.push(t);return n}let r=t.target;for(;r;)v(r)&&n.push(r),r=r.parentNode}catch(t){}return n})(n);if(0===o.length)return;const c=(()=>{try{return"undefined"!=typeof location?location.pathname+location.search:""}catch(t){return""}})();for(let r=0;r<e.length;r++){const s=e[r];if(s.urlScope&&!f(s.urlScope,c))continue;let a=null;for(let t=0;t<o.length;t++)if(x(s,o[t])){a=o[t];break}if(!a)continue;const d=s.rule.id;if((l.get(d)||0)>=s.maxPerPageview)return;const h=d+"#"+i(a),y=Date.now(),v=u.get(h);if(void 0!==v&&y-v<s.dedupeWindowMs)return;if(s.samplingRate<1&&Math.random()>=s.samplingRate)return;u.set(h,y),l.set(d,(l.get(d)||0)+1);const p=k(s.rule,a,n);return void j(()=>t.track("button_click",p))}}catch(t){}}try{document.addEventListener("click",h,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",h,!0)}catch(t){}u.clear(),l.clear()}}function g(t){const n=t.match;return n.element?n.element:n.selector||n.id||n.dataAttr||n.classes&&n.classes.length>0||n.destinationUrl?"any":void 0}function x(t,n){if(!((t,n)=>{if(!n)return(t=>{const n=t.tagName;if("BUTTON"===n||"A"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")})(t);if("any"===n)return!0;if("button"===n){const n=t.tagName;if("BUTTON"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")}return"link"===n?"A"===t.tagName:u(t,n)})(n,t.elementSpec))return!1;const e=t.rule.match;return!(t.selector&&!u(n,t.selector)||void 0!==e.id&&!((t,n)=>t.id===n)(n,e.id)||e.classes&&e.classes.length>0&&!((t,n)=>{try{for(let e=0;e<n.length;e++)if(!t.classList.contains(n[e]))return!1;return!0}catch(t){return!1}})(n,e.classes)||e.dataAttr&&!((t,n,e)=>{try{return!!t.hasAttribute(n)&&(void 0===e||t.getAttribute(n)===e)}catch(t){return!1}})(n,e.dataAttr.name,e.dataAttr.value)||t.text&&!((t,n)=>{const e=i(t);if("regex"===n.mode)return!!n.re&&b(n.re,e);const r=n.ci?e.toLowerCase():e;return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.text)||t.dest&&!((t,n)=>{const e=l(t);if(!e)return!1;if("regex"===n.mode)return!!n.re&&b(n.re,e);if("domain"===n.mode)try{return new URL(e).host.toLowerCase()===n.needle}catch(t){return!1}const r=e.toLowerCase();return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.dest))}function k(t,n,e){const r={},c=t.event.staticProps;if(c)for(const t of Object.keys(c))r[t]=c[t];const u=t.capture;if(u)try{if(u.text){const t=y(i(n));t&&(r.text=t)}if(u.href){const t=l(n);t&&(r.href=t)}if(u.elementId&&n.id&&(r.element_id=n.id),u.classes&&(r.element_classes=Array.from(n.classList)),u.dataAttrs&&u.dataAttrs.length>0)for(let t=0;t<u.dataAttrs.length;t++){const e=u.dataAttrs[t],o=n.getAttribute(e);null!==o&&(r[e]=y(o))}if(u.valueFrom){const t=((t,n)=>{try{const e=t.querySelector(n.selector)||(t.parentElement?t.parentElement.querySelector(n.selector):null);if(!e)return null;const r=n.attr?e.getAttribute(n.attr):e.textContent||"";if(null===r)return null;const i=y(r.replace(/\s+/g," ").trim());return i.length>o?i.slice(0,o):i}catch(t){return null}})(n,u.valueFrom);null!==t&&(r[u.valueFrom.as]=t)}u.selectorPath&&(r.selector_path=((t,n=5)=>{try{const e=[];let r=t,o=0;for(;r&&1===r.nodeType&&o<n;){let t=r.tagName.toLowerCase();if(r.id){e.unshift(t+"#"+r.id);break}const n=Array.from(r.classList).slice(0,2).join(".");n&&(t+="."+n),e.unshift(t),r=r.parentElement,o++}return e.join(" > ")}catch(t){return""}})(n)),u.position&&(r.x=e.clientX,r.y=e.clientY)}catch(t){}return r.rule_id=t.id,t.name&&(r.rule_name=t.name),t.event&&t.event.name&&(r.event_name=t.event.name),r.tag=n.tagName.toLowerCase(),r}function j(t){try{t()}catch(t){}}function O(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}function S(t,n){return"number"!=typeof t||isNaN(t)?n:t}function A(t){return t<0?0:t>1?1:t}const T="__cnvPatched";function I(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}const _=["q","s","query","search","keyword"],P={spa:!0,outboundClicks:!0,scroll:!0,downloads:!0,siteSearch:!0},M={spa:!1,outboundClicks:!1,scroll:!1,downloads:!1,siteSearch:!1};function D(t){try{t()}catch(t){}}function U(t,n,e,r){return new(e||(e=Promise))((o,i)=>{function c(t){try{l(r.next(t))}catch(t){i(t)}}function u(t){try{l(r.throw(t))}catch(t){i(t)}}function l(t){var n;t.done?o(t.value):(n=t.value,n instanceof e?n:new e(t=>{t(n)})).then(c,u)}l((r=r.apply(t,n||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;const C="undefined"!=typeof window&&"undefined"!=typeof document;function N(t){if(!C)return null;try{const n=document.cookie;if(!n)return null;const e=encodeURIComponent(t),r=n.split(";");for(let n=0;n<r.length;n++){const o=r[n].indexOf("=");if(o<0)continue;const i=r[n].slice(0,o).trim();if(i===e||i===t)try{return decodeURIComponent(r[n].slice(o+1))}catch(t){return r[n].slice(o+1)}}return null}catch(t){return null}}function $(t,n,e={}){var r,o,i;if(!C)return!1;try{let c=`${encodeURIComponent(t)}=${encodeURIComponent(n)}`;if(c+=`; path=${null!==(r=e.path)&&void 0!==r?r:"/"}`,e.domain&&(c+=`; domain=${e.domain}`),"number"==typeof e.maxAgeDays){const t=Math.floor(86400*e.maxAgeDays);c+=`; max-age=${t}`,c+=`; expires=${new Date(Date.now()+1e3*t).toUTCString()}`}return c+=`; SameSite=${null!==(o=e.sameSite)&&void 0!==o?o:"Lax"}`,(null!==(i=e.secure)&&void 0!==i?i:"undefined"!=typeof location&&"https:"===location.protocol)&&(c+="; Secure"),document.cookie=c,N(t)===n}catch(t){return!1}}function L(){if(!C)return!1;try{const t="__cnv_probe__";return localStorage.setItem(t,"1"),localStorage.removeItem(t),!0}catch(t){return!1}}function R(t){if(!L())return null;try{return localStorage.getItem(t)}catch(t){return null}}function q(t,n){if(!L())return!1;try{return localStorage.setItem(t,n),!0}catch(t){return!1}}function z(t){if(!L())return!1;try{return localStorage.removeItem(t),!0}catch(t){return!1}}function E(){if(!C)return!1;try{const t="__cnv_probe__";return sessionStorage.setItem(t,"1"),sessionStorage.removeItem(t),!0}catch(t){return!1}}const B={path:"/config",timeoutMs:4e3,ttlMs:3e5};function J(t,n){var e,r;const o=null!=n?n:Object.assign({enabled:!0},B);return void 0===t?Object.assign({},o):!0===t?Object.assign(Object.assign({},o),{enabled:!0}):!1===t?Object.assign(Object.assign({},o),{enabled:!1}):{enabled:null===(e=t.enabled)||void 0===e||e,path:null!==(r=t.path)&&void 0!==r?r:o.path,timeoutMs:"number"==typeof t.timeoutMs?t.timeoutMs:o.timeoutMs,ttlMs:"number"==typeof t.ttlMs?t.ttlMs:o.ttlMs}}function W(t){return"_cnv_cfg_"+t}function Z(t){const n=R(W(t));if(!n)return null;try{const t=JSON.parse(n);return t&&"object"==typeof t&&t.envelope?t:null}catch(t){return null}}function G(t,n,e){try{const r={etag:n,envelope:e,fetchedAt:Date.now()};q(W(t),JSON.stringify(r))}catch(t){}}function F(t){const n=null==t?void 0:t.buttonTracking;if(!n||!1===n.enabled)return null;const e=Array.isArray(n.rules)?n.rules:[];return 0===e.length?null:{rules:e,globalThrottle:n.globalThrottle}}const Q=["gclid","fbclid","ttclid","msclkid","dclid"],V=[["utm_source","source"],["utm_medium","medium"],["utm_campaign","name"],["utm_term","term"],["utm_content","content"],["utm_id","id"]];function H(t,n){try{return t()}catch(t){return n}}function K(){const t=H(()=>window.location.href,"");let n="",e="",r="";if(t)try{const o=new URL(t);n=o.pathname,e=o.search,r=o.hash}catch(t){}return{url:t,path:n,search:e,hash:r,title:H(()=>document.title,""),referrer:H(()=>document.referrer,"")}}function X(t){var n,e;let r="@cnv-vn/track",o="0.0.0-dev";try{r="@cnv-vn/track"}catch(t){}try{o="0.2.0-beta.7"}catch(t){}return{name:null!==(n=null==t?void 0:t.name)&&void 0!==n?n:r,version:null!==(e=null==t?void 0:t.version)&&void 0!==e?e:o}}const Y="0123456789abcdef";function tt(t){return Y[t>>>4&15]+Y[15&t]}function nt(){if("undefined"!=typeof crypto){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();if("function"==typeof crypto.getRandomValues){const t=new Uint8Array(16);return crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128,tt(t[0])+tt(t[1])+tt(t[2])+tt(t[3])+"-"+tt(t[4])+tt(t[5])+"-"+tt(t[6])+tt(t[7])+"-"+tt(t[8])+tt(t[9])+"-"+tt(t[10])+tt(t[11])+tt(t[12])+tt(t[13])+tt(t[14])+tt(t[15])}}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const n=16*Math.random()|0;return("x"===t?n:3&n|8).toString(16)})}const et=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function rt(t){return"string"==typeof t&&et.test(t)}const ot="_cnv_cid";function it(t={}){const n=N(ot),e=R(ot);let r=null;return rt(n)?r=n:rt(e)&&(r=e),r||(r=nt()),ct(r,t),r}function ct(t,n={}){var e;$(ot,t,{domain:n.cookieDomain,maxAgeDays:null!==(e=n.cookieMaxAgeDays)&&void 0!==e?e:730,sameSite:"Lax"}),q(ot,t)}const ut="1.0";function lt(){return(new Date).toISOString()}function st(t,n){var e;const r=null!==(e=n.timestamp)&&void 0!==e?e:lt(),o=Object.assign(Object.assign({},n.context),{projectId:n.projectId,schemaVersion:ut}),i={type:t,messageId:nt(),anonymousId:n.anonymousId,timestamp:r,originalTimestamp:r,context:o};return n.userId&&(i.userId=n.userId),n.sessionId&&(i.sessionId=n.sessionId),n.integrations&&(i.integrations=n.integrations),i}const at="_cnv_sid";function ft(t){if(!t)return null;const n=t.lastIndexOf(".");if(n<0)return null;const e=t.slice(0,n),r=Number(t.slice(n+1));return!rt(e)||!Number.isFinite(r)||r<=0?null:{id:e,lastActivity:r}}function dt(t={}){const n=Date.now(),e=(t=>{var n;return 6e4*(null!==(n=t.timeoutMinutes)&&void 0!==n?n:30)})(t),r=ft(N(at)),o=ft((t=>{if(!E())return null;try{return sessionStorage.getItem(t)}catch(t){return null}})(at));let i=null;for(const t of[r,o])t&&(n-t.lastActivity>e||(!i||t.lastActivity>i.lastActivity)&&(i=t));const c=i?{id:i.id,lastActivity:n}:{id:nt(),lastActivity:n};return ht(c,t),c.id}function ht(t,n={}){var e;const r=(t=>`${t.id}.${t.lastActivity}`)(t),o=null!==(e=n.timeoutMinutes)&&void 0!==e?e:30;$(at,r,{domain:n.cookieDomain,maxAgeDays:o/1440,sameSite:"Lax"}),((t,n)=>{if(!E())return!1;try{return sessionStorage.setItem(t,n),!0}catch(t){return!1}})(at,r)}class yt{constructor(t){var n,e;Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"storageKey",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"maxMessages",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"flushing",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"onlineHandler",{enumerable:!0,configurable:!0,writable:!0,value:null}),this.storageKey=null!==(n=t.storageKey)&&void 0!==n?n:"_cnv_rq",this.maxMessages=null!==(e=t.maxMessages)&&void 0!==e?e:100,this.installOnlineListener()}stash(t){if(t&&0!==t.length)try{const n=this.read();for(n.push(t);n.length>1&&vt(n)>this.maxMessages;)n.shift();this.write(n),this.config.debug&&console.log("[cnv-track] retry queued",t.length,"buffered",vt(n))}catch(t){}}flush(){if(this.flushing||"undefined"!=typeof navigator&&!1===navigator.onLine)return Promise.resolve();const t=this.read();if(0===t.length)return Promise.resolve();this.flushing=!0,this.write([]);const n=()=>{this.flushing=!1};return t.reduce((t,n)=>t.then(()=>this.config.resend(n)),Promise.resolve()).then(n,n)}size(){return vt(this.read())}clear(){try{z(this.storageKey)}catch(t){}}destroy(){if(this.onlineHandler){try{window.removeEventListener("online",this.onlineHandler)}catch(t){}this.onlineHandler=null}}installOnlineListener(){if("undefined"==typeof window)return;const t=()=>{this.flush()};try{window.addEventListener("online",t),this.onlineHandler=t}catch(t){}}read(){const t=R(this.storageKey);if(!t)return[];try{const n=JSON.parse(t);return Array.isArray(n)?n.filter(t=>Array.isArray(t)&&t.length>0):[]}catch(t){return[]}}write(t){0!==t.length?q(this.storageKey,JSON.stringify(t)):z(this.storageKey)}}function vt(t){let n=0;for(const e of t)n+=e.length;return n}class pt{constructor(t){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"buffer",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"timer",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"destroyed",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"handlers",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"retryQueue",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.retryQueue=new yt({debug:t.debug,resend:t=>this.send(t)}),this.installPageLifecycleHandlers(),this.retryQueue.flush()}setConfig(t){this.config=Object.assign(Object.assign({},this.config),t)}enqueue(t){this.destroyed||(this.buffer.push(t),this.buffer.length>=this.config.flushAt?this.flush():this.timer||(this.timer=setTimeout(()=>{this.timer=null,this.flush()},this.config.flushInterval)))}flush(){if(this.timer&&(clearTimeout(this.timer),this.timer=null),0===this.buffer.length)return Promise.resolve();const t=this.buffer;return this.buffer=[],this.send(t)}bufferedCount(){return this.buffer.length}destroy(){this.destroyed||(this.destroyed=!0,this.timer&&(clearTimeout(this.timer),this.timer=null),this.uninstallPageLifecycleHandlers(),this.retryQueue.destroy())}send(t){try{return this.config.useBatch?this.sendBatch(t):Promise.all(t.map(t=>this.sendSingle(t))).then(()=>{})}catch(t){return this.config.debug&&console.warn("[cnv-track] send failed synchronously; dropped batch",t),Promise.resolve()}}sendBatch(t){var n;const e=lt(),r={batch:t.map(t=>mt(t,e)),context:Object.assign(Object.assign({},null!==(n=this.config.batchContext)&&void 0!==n?n:{}),{projectId:this.config.projectId,sentAt:e}),sentAt:e};return this.post(this.batchUrl(),r,t)}sendSingle(t){const n=mt(t,lt());return this.post(this.singleUrl(t.type),n,[t])}batchUrl(){return this.config.endpoint+this.config.batchPath+"?projectId="+encodeURIComponent(this.config.projectId)}singleUrl(t){const n=this.config.singlePath.replace("{type}",t);return this.config.endpoint+n+"?projectId="+encodeURIComponent(this.config.projectId)}post(t,n,e){const r=JSON.stringify(n),o=((t,n)=>{try{return new Blob([t],{type:n})}catch(t){return null}})(r,this.config.contentType),i=o?o.size:(t=>{try{return new Blob([t]).size}catch(n){return t.length}})(r),c=i>65536?null:e;return c&&"undefined"!=typeof navigator&&!1===navigator.onLine?(this.retryQueue.stash(c),this.config.debug&&console.log("[cnv-track] offline; queued for retry",t),Promise.resolve()):o&&this.sendViaBeacon(t,o)?(this.config.debug&&console.log("[cnv-track] beacon",t),Promise.resolve()):this.sendViaFetch(t,r,c)}sendViaBeacon(t,n){if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;if(n.size>65536)return!1;try{return navigator.sendBeacon(t,n)}catch(t){return!1}}sendViaFetch(t,n,e){return"function"!=typeof fetch?(this.config.debug&&console.warn("[cnv-track] no transport available; queued for retry"),e&&this.retryQueue.stash(e),Promise.resolve()):fetch(t,{method:"POST",headers:{"Content-Type":this.config.contentType},body:n,keepalive:!0,credentials:"omit",mode:"cors"}).then(n=>{this.config.debug&&console.log("[cnv-track] fetch flushed",t,n.status)}).catch(n=>{this.config.debug&&console.warn("[cnv-track] flush failed; queued for retry",t,n),e&&this.retryQueue.stash(e)})}installPageLifecycleHandlers(){if("undefined"==typeof window||"undefined"==typeof document)return;const t=()=>{this.flush()},n=()=>{"hidden"===document.visibilityState&&this.flush()};try{window.addEventListener("pagehide",t),document.addEventListener("visibilitychange",n),this.handlers={pagehide:t,visibility:n}}catch(t){}}uninstallPageLifecycleHandlers(){if(this.handlers){try{window.removeEventListener("pagehide",this.handlers.pagehide),document.removeEventListener("visibilitychange",this.handlers.visibility)}catch(t){}this.handlers=null}}}function mt(t,n){return Object.assign(Object.assign({},t),{sentAt:n})}const bt="_cnv_uid",wt="_cnv_traits";function gt(){return N(bt)||R(bt)}const xt={endpoint:"https://tracking.cnvcdp.com/js/v1",batchPath:"/batch",singlePath:"/{type}",useBatch:!0,contentType:"text/plain",debug:!1,allowAdTracking:!0,cookieMaxAgeDays:730,sessionTimeoutMinutes:30,flushAt:20,flushInterval:5e3,autoPageview:!0,autotrack:!1,integrations:{},remoteConfig:J(void 0)},kt={analytics:!0,marketing:!0};function jt(t,n){if(!Array.isArray(n)||0===n.length)return;const e=n;try{switch(e[0]){case"init":t.init(e[1],e[2]);break;case"pageview":t.pageview(e[1],e[2]);break;case"page":t.page(e[1],e[2]);break;case"screen":t.screen(e[1],e[2]);break;case"track":t.track(e[1],e[2]);break;case"identify":t.identify(e[1],e[2]);break;case"alias":t.alias(e[1],e[2]);break;case"group":t.group(e[1],e[2]);break;case"reset":t.reset();break;case"flush":t.flush();break;case"consent":t.consent(e[1]);break;case"set":t.set(e[1])}}catch(t){}}const Ot=/^(https:|zalo:)/i;class St extends Error{constructor(t){super(`loginWithZalo() thất bại: ${t}`),Object.defineProperty(this,"reason",{enumerable:!0,configurable:!0,writable:!0,value:t}),this.name="ZaloQrLoginError"}}function At(t,n,e,r){return`${t}${n}?${new URLSearchParams(Object.assign({projectId:e},r)).toString()}`}function Tt(t,n){return U(this,void 0,void 0,function*(){var e,r,o;if(!t.projectId||!t.endpoint)throw new St("not-initialized");if(!t.zaloQrLoginEnabled)throw new St("disabled");if(!t.consentGranted)throw new St("consent-required");const i={preferSse:null===(e=null==n?void 0:n.preferSse)||void 0===e||e,pollIntervalMs:null!==(r=null==n?void 0:n.pollIntervalMs)&&void 0!==r?r:2e3,autoIdentify:null===(o=null==n?void 0:n.autoIdentify)||void 0===o||o},c=t.projectId,u=t.endpoint,l=yield function(t,n){return U(this,void 0,void 0,function*(){const e="undefined"!=typeof AbortController?new AbortController:null,r=e?setTimeout(()=>e.abort(),8e3):null;let o;try{o=yield fetch(At(t,"/zalo-qr/session",n),{method:"POST",credentials:"omit",mode:"cors",signal:e?e.signal:void 0})}catch(t){throw new St("network")}finally{r&&clearTimeout(r)}if(!o.ok)throw new St(401===(i=o.status)?"invalid-project-key":403===i?"project-suspended":"network");var i;return(t=>{const n=t;if(!n||"string"!=typeof n.sessionToken||!n.sessionToken||"string"!=typeof n.miniAppDeepLink||!Ot.test(n.miniAppDeepLink)||"number"!=typeof n.expiresInSeconds||!Number.isFinite(n.expiresInSeconds)||n.expiresInSeconds<=0)throw new St("network");return{sessionToken:n.sessionToken,miniAppDeepLink:n.miniAppDeepLink,expiresInSeconds:n.expiresInSeconds}})(yield o.json())})}(u,c),s=((t,n,e,r,o)=>{let i=!1,c=null,u=null,l=null,s=null,a=null;const f=()=>{c&&(c.close(),c=null),null!==u&&(clearInterval(u),u=null),null!==l&&(clearTimeout(l),l=null),null!==s&&(clearTimeout(s),s=null)};return{promise:new Promise((d,h)=>{a=h;const y=t=>{i||(i=!0,f(),t())},v=t=>{"success"===t.status?y(()=>d((t=>({contactId:t.contactId,fullName:t.fullName,level:t.level,xp:t.xp}))(t))):"denied"===t.status?y(()=>h(new St("denied"))):"expired"===t.status&&y(()=>h(new St("expired")))},p=()=>{null!==u||i||(u=setInterval(()=>{fetch(At(t,"/zalo-qr/status",n,{sessionToken:e}),{method:"GET",credentials:"omit",mode:"cors"}).then(t=>404===t.status?v({status:"expired"}):t.ok?t.json().then(t=>v(t)):void 0).catch(()=>{})},o.pollIntervalMs))};if(o.preferSse&&"undefined"!=typeof EventSource)try{c=new EventSource(At(t,"/zalo-qr/stream",n,{sessionToken:e})),c.addEventListener("success",t=>{try{v(JSON.parse(t.data))}catch(t){}}),c.addEventListener("denied",()=>v({status:"denied"})),c.onerror=()=>{null!==s||i||(s=setTimeout(()=>{s=null,i||(c&&(c.close(),c=null),p())},3e3))}}catch(t){p()}else p();l=setTimeout(()=>{y(()=>h(new St("expired")))},1e3*r)}),cancel(){i||(i=!0,f(),null==a||a(new St("cancelled")))}}})(u,c,l.sessionToken,l.expiresInSeconds,i),a=s.promise.then(n=>{try{if(i.autoIdentify&&null!=n.contactId){const e=String(n.contactId),r=t.getUserId();null!=r&&r!==e?t.alias(e,r):t.identify(e,Object.assign(Object.assign(Object.assign({},n.fullName?{name:n.fullName}:{}),n.level?{level:n.level}:{}),null!=n.xp?{xp:n.xp}:{}))}}catch(t){}return n});return{sessionToken:l.sessionToken,qrPayload:l.miniAppDeepLink,expiresInSeconds:l.expiresInSeconds,result:a,cancel:s.cancel,retry(){return s.cancel(),Tt(t,n)}}})}const It="0.2.0-beta.7",_t="@cnv-vn/track",Pt=function(){let o=null,i=Object.assign({},xt),c=Object.assign({},kt),u=null,l=!1,s=null,a=null;function f(t){const n=Object.assign({},i);return t?(void 0!==t.endpoint&&(n.endpoint=(e=t.endpoint).endsWith("/")?e.slice(0,-1):e),void 0!==t.batchPath&&(n.batchPath=t.batchPath),void 0!==t.singlePath&&(n.singlePath=t.singlePath),void 0!==t.useBatch&&(n.useBatch=t.useBatch),void 0!==t.contentType&&(n.contentType=t.contentType),void 0!==t.debug&&(n.debug=t.debug),void 0!==t.allowAdTracking&&(n.allowAdTracking=t.allowAdTracking),void 0!==t.cookieDomain&&(n.cookieDomain=t.cookieDomain),void 0!==t.cookieMaxAgeDays&&(n.cookieMaxAgeDays=t.cookieMaxAgeDays),void 0!==t.sessionTimeoutMinutes&&(n.sessionTimeoutMinutes=t.sessionTimeoutMinutes),void 0!==t.flushAt&&(n.flushAt=t.flushAt),void 0!==t.flushInterval&&(n.flushInterval=t.flushInterval),void 0!==t.autoPageview&&(n.autoPageview=t.autoPageview),void 0!==t.autotrack&&(n.autotrack=t.autotrack),void 0!==t.integrations&&(n.integrations=t.integrations),void 0!==t.remoteConfig&&(n.remoteConfig=J(t.remoteConfig,i.remoteConfig)),n):n;var e}function d(){return u||(u=new pt({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,projectId:null!=o?o:"",debug:i.debug}),u)}function h(){var t;if(!o)return null;const n=((t={})=>{var n;const e={page:K(),screen:{width:H(()=>window.screen.width,0),height:H(()=>window.screen.height,0),density:H(()=>window.devicePixelRatio||1,1)},locale:H(()=>navigator.language||"",""),timezone:H(()=>Intl.DateTimeFormat().resolvedOptions().timeZone||"",""),userAgent:H(()=>navigator.userAgent||"",""),library:X(t.library)},r=((t,n=!0)=>{const e=H(()=>window.location.search,"");if(!e)return;let r;try{r=new URLSearchParams(e)}catch(t){return}const o={};let i=!1;for(const[t,n]of V){const e=r.get(t);e&&e.length>0&&(o[n]=e,i=!0)}if(n){const t={};for(const n of Q){const e=r.get(n);e&&e.length>0&&(t[n]=e)}Object.keys(t).length>0&&(o.clickIds=t,i=!0)}return i?o:void 0})(0,null===(n=t.allowAdTracking)||void 0===n||n);return r&&(e.campaign=r),e})({allowAdTracking:i.allowAdTracking});return c.analytics&&c.marketing||(n.consent=Object.assign({},c)),{anonymousId:it({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),sessionId:dt({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),userId:null!==(t=gt())&&void 0!==t?t:void 0,context:n,projectId:o,integrations:Object.keys(i.integrations).length>0?i.integrations:void 0}}function y(t){i.debug&&console.warn(`[cnv-track] ${t}() called before init()`)}function v(t,n){try{if("string"!=typeof t||0===t.length)return;const e=h();if(!e)return y("track");const r=((t,n,e)=>{const r=st("track",e);return r.event=t,r.properties=null!=n?n:{},r})(t,n,e);d().enqueue(r)}catch(t){}}return{get initialized(){return l},get projectId(){return o},get endpoint(){return i.endpoint},isZaloQrLoginEnabled:()=>!!o&&(t=>{const n=Z(t);return!n||!1!==(null===(r=null==(e=n.envelope)?void 0:e.zaloQrLogin)||void 0===r?void 0:r.enabled);var e,r})(o),get consentGranted(){return c.analytics&&c.marketing},init(h,y){try{if(!h||"string"!=typeof h)return;if(i=f(y),(null==y?void 0:y.consent)&&(c=Object.assign(Object.assign({},c),y.consent)),o=h,u&&u.destroy(),u=null,d(),l=!0,it({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),dt({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),s){try{s()}catch(t){}s=null}const p={page(t,n){},track(t,n){return v(t,n)}};if(i.autotrack){let o=i.autotrack;i.remoteConfig.enabled&&"object"==typeof o&&o.buttonTracking&&(o=Object.assign(Object.assign({},o),{buttonTracking:void 0})),s=function(o,i){const c=void 0===(u=i)||!1===u?Object.assign(Object.assign({},M),{searchParams:_}):!0===u?Object.assign(Object.assign({},P),{searchParams:_}):{spa:null===(l=u.spa)||void 0===l||l,outboundClicks:null===(s=u.outboundClicks)||void 0===s||s,scroll:null===(a=u.scroll)||void 0===a||a,downloads:null===(f=u.downloads)||void 0===f||f,siteSearch:null===(d=u.siteSearch)||void 0===d||d,downloadExtensions:u.downloadExtensions,searchParams:null!==(h=u.searchParams)&&void 0!==h?h:_,buttonTracking:u.buttonTracking};var u,l,s,a,f,d,h;c.outboundClicks=!1,c.downloads=!1,c.scroll=!1,c.siteSearch=!1;const y=[];let v=null;function p(t){if(c.siteSearch)try{const n=null!=t?t:(()=>{try{return"undefined"!=typeof location?location.href:""}catch(t){return""}})();if(n===v)return;v=n;const e=((t,n=_)=>{try{if(!t){if("undefined"==typeof location)return null;t=location.href}const e=t instanceof URL?t:new URL(t,(()=>{try{return"undefined"!=typeof location?location.href:"http://localhost/"}catch(t){return"http://localhost/"}})());if(!e.search)return null;for(const t of n){const n=e.searchParams.get(t);if(null===n)continue;const r=n.trim();if(0!==r.length)return{query:r,param:t}}return null}catch(t){return null}})(n,c.searchParams);e&&D(()=>o.track("Site Searched",{query:e.query,param:e.param}))}catch(t){}}if(c.outboundClicks||c.downloads){const i=(o=>{var i;if("undefined"==typeof document)return()=>{};const c=new Set((null!==(i=o.downloadExtensions)&&void 0!==i?i:t).map(t=>t.toLowerCase())),u=!!o.onOutbound,l=!!o.onDownload;if(!u&&!l)return()=>{};function s(t){var i,s;try{if(0!==t.button)return;if(t.defaultPrevented)return;const a=(t=>{const e="function"==typeof t.composedPath?t.composedPath():[];if(e.length>0)for(const t of e)if(n(t))return t;let r=t.target;for(;r;){if(n(r))return r;r=r.parentNode}return null})(t);if(!a)return;const f=a.getAttribute("href");if(!f)return;if(/^(javascript:|mailto:|tel:|sms:)/i.test(f))return;if(f.startsWith("#"))return;const d=(t=>{try{return new URL(t,location.href)}catch(t){return null}})(f);if(!d)return;let h=!1;if(l){const t=a.hasAttribute("download"),n=(t=>{const n=e(t),r=n.lastIndexOf(".");return r<=0||r===n.length-1?null:n.slice(r+1).toLowerCase()})(d.pathname);(t||n&&c.has(n))&&(h=!0,null===(i=o.onDownload)||void 0===i||i.call(o,{url:d.href,fileName:e(d.pathname),fileExtension:null!=n?n:"",text:r(a)}))}u&&!h&&d.host!==(()=>{try{return"undefined"!=typeof location?location.hostname:""}catch(t){return""}})()&&d.host&&(null===(s=o.onOutbound)||void 0===s||s.call(o,{url:d.href,host:d.host,text:r(a),target:a.target||"_self"}))}catch(t){}}try{document.addEventListener("click",s,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",s,!0)}catch(t){}}})({downloadExtensions:c.downloadExtensions,onOutbound:c.outboundClicks?t=>D(()=>o.track("Clicked",t)):void 0,onDownload:c.downloads?t=>D(()=>o.track("File Downloaded",t)):void 0});y.push(i)}c.buttonTracking&&c.buttonTracking.rules&&c.buttonTracking.rules.length>0&&y.push(w(o,c.buttonTracking));let m=null;if(c.scroll&&(m=(t=>{if("undefined"==typeof window||"undefined"==typeof document)return{destroy(){},reset(){}};let n=!1,e=null,r=!1;function o(){if(e=null,!r&&!n)try{const e=document.documentElement,r=document.body,o=Math.max(e?e.scrollHeight:0,r?r.scrollHeight:0),i=Math.max(e?e.clientHeight:0,window.innerHeight||0);if(o<=i)return n=!0,c(),void t(90);((window.scrollY||(e?e.scrollTop:0)||0)+i)/o>=.9&&(n=!0,c(),t(90))}catch(t){}}function i(){if(null===e)try{e=window.requestAnimationFrame(o)}catch(t){o()}}function c(){try{window.removeEventListener("scroll",i),null!==e&&window.cancelAnimationFrame(e),e=null}catch(t){}}try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}return{destroy(){r=!0,c()},reset(){if(!r){n=!1;try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}}}}})(t=>{D(()=>o.track("Scrolled",{percent:t}))}),y.push(()=>null==m?void 0:m.destroy())),c.spa){const t=[],n=function(){if("undefined"==typeof window||"undefined"==typeof history)return()=>{};let n=I();function e(){try{const e=I();if(e===n)return;n=e,(n=>{const e=setTimeout(()=>{const n=t.indexOf(e);-1!==n&&t.splice(n,1),D(()=>o.page())},0);t.push(e),null==m||m.reset(),p(n)})(e)}catch(t){}}const r=history.pushState,i=history.replaceState,c=!0===r[T],u=!0===i[T];if(!c){function a(...t){r.apply(this,t),e()}a[T]=!0,history.pushState=a}if(!u){function f(...t){i.apply(this,t),e()}f[T]=!0,history.replaceState=f}const l=()=>e(),s=()=>e();try{window.addEventListener("popstate",l),window.addEventListener("hashchange",s)}catch(d){}return()=>{try{window.removeEventListener("popstate",l),window.removeEventListener("hashchange",s)}catch(t){}try{!c&&history.pushState[T]&&(history.pushState=r),!u&&history.replaceState[T]&&(history.replaceState=i)}catch(t){}}}();y.push(()=>{for(let n=0;n<t.length;n++)clearTimeout(t[n]);t.length=0,n()})}return p(),()=>{for(;y.length>0;){const t=y.pop();try{null==t||t()}catch(t){}}v=null}}(p,o)}if(a){try{a()}catch(t){}a=null}if(i.remoteConfig.enabled&&o){const t=t=>{if(a){try{a()}catch(t){}a=null}t&&t.rules&&t.rules.length>0&&(a=w(p,t))},n=(t=>{const n=Z(t);return n?F(n.envelope):null})(o);n&&t(n),function(t){return U(this,void 0,void 0,function*(){if(!t.projectId)return{status:"error"};const n=Z(t.projectId);if(n&&Date.now()-n.fetchedAt<t.ttlMs)return{status:"unchanged"};const e=yield function(t){return U(this,void 0,void 0,function*(){var n;if("function"!=typeof fetch)return null;const e=t.endpoint+t.path+"?projectId="+encodeURIComponent(t.projectId),r="undefined"!=typeof AbortController?new AbortController:null,o=r?setTimeout(()=>r.abort(),t.timeoutMs):null;try{const o={};t.etag&&(o["If-None-Match"]=t.etag);const i=yield fetch(e,{method:"GET",headers:o,credentials:"omit",mode:"cors",signal:r?r.signal:void 0});if(304===i.status)return{envelope:null,etag:null!==(n=t.etag)&&void 0!==n?n:"",notModified:!0};if(!i.ok)return null;const c=i.headers&&"function"==typeof i.headers.get?i.headers.get("ETag"):null;return{envelope:yield i.json(),etag:null!=c?c:"",notModified:!1}}catch(t){return null}finally{o&&clearTimeout(o)}})}({endpoint:t.endpoint,path:t.path,projectId:t.projectId,timeoutMs:t.timeoutMs,etag:null==n?void 0:n.etag});return e?e.notModified?(n&&G(t.projectId,n.etag,n.envelope),{status:"unchanged"}):e.envelope?(G(t.projectId,e.etag,e.envelope),{status:"updated",buttonTracking:F(e.envelope)}):{status:"error"}:{status:"error"}})}({endpoint:i.endpoint,path:i.remoteConfig.path,projectId:o,timeoutMs:i.remoteConfig.timeoutMs,ttlMs:i.remoteConfig.ttlMs}).then(n=>{"updated"===n.status&&t(n.buttonTracking)}).catch(()=>{})}}catch(t){}},pageview(t,n){},page(t,n){},screen(t,n){},track:v,identify(t,n){try{if("string"!=typeof t||0===t.length)return;((t,n,e={})=>{var r,o;if(t&&($(bt,t,{domain:e.cookieDomain,maxAgeDays:null!==(r=e.cookieMaxAgeDays)&&void 0!==r?r:730,sameSite:"Lax"}),q(bt,t),null!=n)){const t=null!==(o=(()=>{const t=R(wt);if(!t)return null;try{const n=JSON.parse(t);return n&&"object"==typeof n&&!Array.isArray(n)?n:null}catch(t){return null}})())&&void 0!==o?o:{},e=Object.assign(Object.assign({},t),n);try{q(wt,JSON.stringify(e))}catch(t){}}})(t,n,{cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays});const e=h();if(!e)return y("identify");const r=((t,n,e)=>{var r;const o=st("identify",e);return t&&(o.userId=t),o.traits=null!==(r=n)&&void 0!==r?r:{},o})(t,n,e);d().enqueue(r)}catch(t){}},alias(t,n){},group(t,n){},reset(){try{((t={})=>{((t,n={})=>{var e;if(!C)return!1;try{let r=`${encodeURIComponent(t)}=`;return r+=`; path=${null!==(e=n.path)&&void 0!==e?e:"/"}`,n.domain&&(r+=`; domain=${n.domain}`),r+="; max-age=0",r+="; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=r,null===N(t)}catch(t){return!1}})(bt,{domain:t.cookieDomain}),z(bt),z(wt)})({cookieDomain:i.cookieDomain}),((t={})=>{ct(nt(),t)})({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),((t={})=>{ht({id:nt(),lastActivity:Date.now()},t)})({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes})}catch(t){}},flush(){try{u&&u.flush()}catch(t){}},consent(t){try{c=Object.assign(Object.assign({},c),t)}catch(t){}},set(t){try{i=f(t),t.consent&&(c=Object.assign(Object.assign({},c),t.consent)),u&&u.setConfig({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,debug:i.debug})}catch(t){}},destroy(){if(s){try{s()}catch(t){}s=null}if(a){try{a()}catch(t){}a=null}u&&u.destroy(),u=null,l=!1,o=null,i=Object.assign({},xt),c=Object.assign({},kt)}}}();function Mt(t){return Tt({projectId:Pt.projectId,endpoint:Pt.endpoint,zaloQrLoginEnabled:Pt.isZaloQrLoginEnabled(),consentGranted:Pt.consentGranted,identify:Pt.identify,alias:Pt.alias,getUserId:gt},t)}"undefined"!=typeof window&&(t=>{if("undefined"==typeof window)return()=>{};const n=window.cnvQ;if(n&&n.__loaded)return()=>{};const e=window.cnvQ=null!=n?n:[],r=e.splice(0,e.length);e.push.bind(e),e.push=(...n)=>{for(const e of n)jt(t,e);return e.length},e.__loaded=!0;for(const n of r)jt(t,n)})(Pt);const Dt=Object.assign(Pt,{version:It,name:_t,schemaVersion:ut,loginWithZalo:Mt});export{_t as NAME,ut as SCHEMA_VERSION,It as VERSION,St as ZaloQrLoginError,Dt as default,Mt as loginWithZalo};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @cnv-vn/track v0.2.0-beta.7
|
|
3
|
+
* (c) 2026 CNV
|
|
4
|
+
* Released under the UNLICENSED license.
|
|
5
|
+
*/
|
|
6
|
+
!function(t){const n=["pdf","doc","docx","xls","xlsx","ppt","pptx","txt","csv","rtf","zip","rar","7z","tar","gz","mp3","mp4","mov","wav","avi","mkv","dmg","exe","pkg","apk","msi","svg"];function e(t){return!!t&&"object"==typeof t&&1===t.nodeType&&"A"===t.tagName}function r(t){const n=t.lastIndexOf("/");return-1===n?t:t.slice(n+1)}function o(t){var n,e,r;let o=(null!==(n=t.textContent)&&void 0!==n?n:"").trim();return o||(o=(null!==(e=t.getAttribute("aria-label"))&&void 0!==e?e:"").trim()),o||(o=(null!==(r=t.getAttribute("title"))&&void 0!==r?r:"").trim()),o.length>200?o.slice(0,200):o}const i=200;function c(t){let n="";try{n=(t.textContent||"").replace(/\s+/g," ").trim(),n||(n=(t.getAttribute("aria-label")||"").trim()),n||(n=(t.getAttribute("title")||"").trim()),n=n.normalize("NFC")}catch(t){return""}return n.length>i?n.slice(0,i):n}function u(t){const n=!1!==t.caseInsensitive;if("regex"===t.mode)return{mode:"regex",needle:t.value,ci:n,re:b(t.value,n)};const e=t.value.normalize("NFC");return{mode:t.mode,needle:n?e.toLowerCase():e,ci:n,re:null}}function l(t,n){try{return t.matches(n)}catch(t){return!1}}function s(t){try{if("A"===t.tagName){const n=t.getAttribute("href");return n?m(n):null}const n=t.getAttribute("formaction");if(n)return m(n);const e=t.getAttribute("data-href");if(e)return m(e);const r=t.closest("form");if(r){const t=r.getAttribute("action");if(t)return m(t)}return null}catch(t){return null}}function a(t){return"regex"===t.mode?{mode:"regex",needle:t.value,re:b(t.value,!0)}:{mode:t.mode,needle:t.value.toLowerCase(),re:null}}function f(t){if("startsWith"===t.mode)return{mode:"startsWith",needle:t.value,re:null};const n="glob"===t.mode?"^"+t.value.replace(/[.+^${}()|[\]\\?]/g,"\\$&").replace(/\*/g,".*")+"$":t.value;return{mode:t.mode,needle:t.value,re:b(n,!1)}}function d(t,n){return"startsWith"===t.mode?0===n.indexOf(t.needle):!t.re||w(t.re,n)}const h=/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,y=/\+?\d[\d\s().-]{7,}\d/g;function v(t){if(!t)return t;try{return t.replace(h,"[email]").replace(y,"[phone]")}catch(n){return t}}function p(t){return!!t&&"object"==typeof t&&1===t.nodeType}function m(t){try{return new URL(t,location.href).href}catch(n){return t}}function b(t,n){try{return!t||t.length>1e3?null:new RegExp(t,n?"i":"")}catch(t){return null}}function w(t,n){try{return t.test(n)}catch(t){return!1}}function g(t,n){if("undefined"==typeof document)return()=>{};const e=(t=>{const n=t.globalThrottle||{},e=[],r=t.rules||[];for(let t=0;t<r.length;t++){const o=r[t];if(!o||!1===o.enabled)continue;if(!o.match||!o.event)continue;const i=o.match,c=o.throttle||{};e.push({rule:o,elementSpec:x(o),selector:i.selector||null,text:i.text?u(i.text):null,dest:i.destinationUrl?a(i.destinationUrl):null,urlScope:o.urlScope?f(o.urlScope):null,dedupeWindowMs:A(c.dedupeWindowMs,A(n.dedupeWindowMs,500)),maxPerPageview:A(c.maxPerPageview,A(n.maxPerPageview,1/0)),samplingRate:T(A(c.samplingRate,A(n.samplingRate,1)))})}return e.sort((t,n)=>(t.rule.priority||0)-(n.rule.priority||0)),e})(n);if(0===e.length)return()=>{};const r=new WeakMap;let o=0;const i=t=>{let n=r.get(t);return void 0===n&&(n=++o,r.set(t,n)),String(n)},c=new Map,l=new Map;let s=S();function h(n){try{if(0!==n.button)return;const r=S();r!==s&&(s=r,l.clear(),c.clear());const o=(t=>{const n=[];try{const e="function"==typeof t.composedPath?t.composedPath():[];if(e&&e.length>0){for(const t of e)p(t)&&n.push(t);return n}let r=t.target;for(;r;)p(r)&&n.push(r),r=r.parentNode}catch(t){}return n})(n);if(0===o.length)return;const u=(()=>{try{return"undefined"!=typeof location?location.pathname+location.search:""}catch(t){return""}})();for(let r=0;r<e.length;r++){const s=e[r];if(s.urlScope&&!d(s.urlScope,u))continue;let a=null;for(let t=0;t<o.length;t++)if(j(s,o[t])){a=o[t];break}if(!a)continue;const f=s.rule.id;if((l.get(f)||0)>=s.maxPerPageview)return;const h=f+"#"+i(a),y=Date.now(),v=c.get(h);if(void 0!==v&&y-v<s.dedupeWindowMs)return;if(s.samplingRate<1&&Math.random()>=s.samplingRate)return;c.set(h,y),l.set(f,(l.get(f)||0)+1);const p=k(s.rule,a,n);return void O(()=>t.track("button_click",p))}}catch(t){}}try{document.addEventListener("click",h,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",h,!0)}catch(t){}c.clear(),l.clear()}}function x(t){const n=t.match;return n.element?n.element:n.selector||n.id||n.dataAttr||n.classes&&n.classes.length>0||n.destinationUrl?"any":void 0}function j(t,n){if(!((t,n)=>{if(!n)return(t=>{const n=t.tagName;if("BUTTON"===n||"A"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")})(t);if("any"===n)return!0;if("button"===n){const n=t.tagName;if("BUTTON"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")}return"link"===n?"A"===t.tagName:l(t,n)})(n,t.elementSpec))return!1;const e=t.rule.match;return!(t.selector&&!l(n,t.selector)||void 0!==e.id&&!((t,n)=>t.id===n)(n,e.id)||e.classes&&e.classes.length>0&&!((t,n)=>{try{for(let e=0;e<n.length;e++)if(!t.classList.contains(n[e]))return!1;return!0}catch(t){return!1}})(n,e.classes)||e.dataAttr&&!((t,n,e)=>{try{return!!t.hasAttribute(n)&&(void 0===e||t.getAttribute(n)===e)}catch(t){return!1}})(n,e.dataAttr.name,e.dataAttr.value)||t.text&&!((t,n)=>{const e=c(t);if("regex"===n.mode)return!!n.re&&w(n.re,e);const r=n.ci?e.toLowerCase():e;return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.text)||t.dest&&!((t,n)=>{const e=s(t);if(!e)return!1;if("regex"===n.mode)return!!n.re&&w(n.re,e);if("domain"===n.mode)try{return new URL(e).host.toLowerCase()===n.needle}catch(t){return!1}const r=e.toLowerCase();return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.dest))}function k(t,n,e){const r={},o=t.event.staticProps;if(o)for(const t of Object.keys(o))r[t]=o[t];const u=t.capture;if(u)try{if(u.text){const t=v(c(n));t&&(r.text=t)}if(u.href){const t=s(n);t&&(r.href=t)}if(u.elementId&&n.id&&(r.element_id=n.id),u.classes&&(r.element_classes=Array.from(n.classList)),u.dataAttrs&&u.dataAttrs.length>0)for(let t=0;t<u.dataAttrs.length;t++){const e=u.dataAttrs[t],o=n.getAttribute(e);null!==o&&(r[e]=v(o))}if(u.valueFrom){const t=((t,n)=>{try{const e=t.querySelector(n.selector)||(t.parentElement?t.parentElement.querySelector(n.selector):null);if(!e)return null;const r=n.attr?e.getAttribute(n.attr):e.textContent||"";if(null===r)return null;const o=v(r.replace(/\s+/g," ").trim());return o.length>i?o.slice(0,i):o}catch(t){return null}})(n,u.valueFrom);null!==t&&(r[u.valueFrom.as]=t)}u.selectorPath&&(r.selector_path=((t,n=5)=>{try{const e=[];let r=t,o=0;for(;r&&1===r.nodeType&&o<n;){let t=r.tagName.toLowerCase();if(r.id){e.unshift(t+"#"+r.id);break}const n=Array.from(r.classList).slice(0,2).join(".");n&&(t+="."+n),e.unshift(t),r=r.parentElement,o++}return e.join(" > ")}catch(t){return""}})(n)),u.position&&(r.x=e.clientX,r.y=e.clientY)}catch(t){}return r.rule_id=t.id,t.name&&(r.rule_name=t.name),t.event&&t.event.name&&(r.event_name=t.event.name),r.tag=n.tagName.toLowerCase(),r}function O(t){try{t()}catch(t){}}function S(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}function A(t,n){return"number"!=typeof t||isNaN(t)?n:t}function T(t){return t<0?0:t>1?1:t}const I="__cnvPatched";function _(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}const P=["q","s","query","search","keyword"],M={spa:!0,outboundClicks:!0,scroll:!0,downloads:!0,siteSearch:!0},D={spa:!1,outboundClicks:!1,scroll:!1,downloads:!1,siteSearch:!1};function U(t){try{t()}catch(t){}}function C(t,n,e,r){return new(e||(e=Promise))((o,i)=>{function c(t){try{l(r.next(t))}catch(t){i(t)}}function u(t){try{l(r.throw(t))}catch(t){i(t)}}function l(t){var n;t.done?o(t.value):(n=t.value,n instanceof e?n:new e(t=>{t(n)})).then(c,u)}l((r=r.apply(t,n||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;const N="undefined"!=typeof window&&"undefined"!=typeof document;function $(t){if(!N)return null;try{const n=document.cookie;if(!n)return null;const e=encodeURIComponent(t),r=n.split(";");for(let n=0;n<r.length;n++){const o=r[n].indexOf("=");if(o<0)continue;const i=r[n].slice(0,o).trim();if(i===e||i===t)try{return decodeURIComponent(r[n].slice(o+1))}catch(t){return r[n].slice(o+1)}}return null}catch(t){return null}}function L(t,n,e={}){var r,o,i;if(!N)return!1;try{let c=`${encodeURIComponent(t)}=${encodeURIComponent(n)}`;if(c+=`; path=${null!==(r=e.path)&&void 0!==r?r:"/"}`,e.domain&&(c+=`; domain=${e.domain}`),"number"==typeof e.maxAgeDays){const t=Math.floor(86400*e.maxAgeDays);c+=`; max-age=${t}`,c+=`; expires=${new Date(Date.now()+1e3*t).toUTCString()}`}return c+=`; SameSite=${null!==(o=e.sameSite)&&void 0!==o?o:"Lax"}`,(null!==(i=e.secure)&&void 0!==i?i:"undefined"!=typeof location&&"https:"===location.protocol)&&(c+="; Secure"),document.cookie=c,$(t)===n}catch(t){return!1}}function R(){if(!N)return!1;try{const t="__cnv_probe__";return localStorage.setItem(t,"1"),localStorage.removeItem(t),!0}catch(t){return!1}}function q(t){if(!R())return null;try{return localStorage.getItem(t)}catch(t){return null}}function z(t,n){if(!R())return!1;try{return localStorage.setItem(t,n),!0}catch(t){return!1}}function E(t){if(!R())return!1;try{return localStorage.removeItem(t),!0}catch(t){return!1}}function B(){if(!N)return!1;try{const t="__cnv_probe__";return sessionStorage.setItem(t,"1"),sessionStorage.removeItem(t),!0}catch(t){return!1}}const J={path:"/config",timeoutMs:4e3,ttlMs:3e5};function W(t,n){var e,r;const o=null!=n?n:Object.assign({enabled:!0},J);return void 0===t?Object.assign({},o):!0===t?Object.assign(Object.assign({},o),{enabled:!0}):!1===t?Object.assign(Object.assign({},o),{enabled:!1}):{enabled:null===(e=t.enabled)||void 0===e||e,path:null!==(r=t.path)&&void 0!==r?r:o.path,timeoutMs:"number"==typeof t.timeoutMs?t.timeoutMs:o.timeoutMs,ttlMs:"number"==typeof t.ttlMs?t.ttlMs:o.ttlMs}}function Z(t){return"_cnv_cfg_"+t}function G(t){const n=q(Z(t));if(!n)return null;try{const t=JSON.parse(n);return t&&"object"==typeof t&&t.envelope?t:null}catch(t){return null}}function F(t,n,e){try{const r={etag:n,envelope:e,fetchedAt:Date.now()};z(Z(t),JSON.stringify(r))}catch(t){}}function Q(t){const n=null==t?void 0:t.buttonTracking;if(!n||!1===n.enabled)return null;const e=Array.isArray(n.rules)?n.rules:[];return 0===e.length?null:{rules:e,globalThrottle:n.globalThrottle}}const V=["gclid","fbclid","ttclid","msclkid","dclid"],H=[["utm_source","source"],["utm_medium","medium"],["utm_campaign","name"],["utm_term","term"],["utm_content","content"],["utm_id","id"]];function K(t,n){try{return t()}catch(t){return n}}function X(){const t=K(()=>window.location.href,"");let n="",e="",r="";if(t)try{const o=new URL(t);n=o.pathname,e=o.search,r=o.hash}catch(t){}return{url:t,path:n,search:e,hash:r,title:K(()=>document.title,""),referrer:K(()=>document.referrer,"")}}function Y(t){var n,e;let r="@cnv-vn/track",o="0.0.0-dev";try{r="@cnv-vn/track"}catch(t){}try{o="0.2.0-beta.7"}catch(t){}return{name:null!==(n=null==t?void 0:t.name)&&void 0!==n?n:r,version:null!==(e=null==t?void 0:t.version)&&void 0!==e?e:o}}const tt="0123456789abcdef";function nt(t){return tt[t>>>4&15]+tt[15&t]}function et(){if("undefined"!=typeof crypto){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();if("function"==typeof crypto.getRandomValues){const t=new Uint8Array(16);return crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128,nt(t[0])+nt(t[1])+nt(t[2])+nt(t[3])+"-"+nt(t[4])+nt(t[5])+"-"+nt(t[6])+nt(t[7])+"-"+nt(t[8])+nt(t[9])+"-"+nt(t[10])+nt(t[11])+nt(t[12])+nt(t[13])+nt(t[14])+nt(t[15])}}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const n=16*Math.random()|0;return("x"===t?n:3&n|8).toString(16)})}const rt=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function ot(t){return"string"==typeof t&&rt.test(t)}const it="_cnv_cid";function ct(t={}){const n=$(it),e=q(it);let r=null;return ot(n)?r=n:ot(e)&&(r=e),r||(r=et()),ut(r,t),r}function ut(t,n={}){var e;L(it,t,{domain:n.cookieDomain,maxAgeDays:null!==(e=n.cookieMaxAgeDays)&&void 0!==e?e:730,sameSite:"Lax"}),z(it,t)}const lt="1.0";function st(){return(new Date).toISOString()}function at(t,n){var e;const r=null!==(e=n.timestamp)&&void 0!==e?e:st(),o=Object.assign(Object.assign({},n.context),{projectId:n.projectId,schemaVersion:lt}),i={type:t,messageId:et(),anonymousId:n.anonymousId,timestamp:r,originalTimestamp:r,context:o};return n.userId&&(i.userId=n.userId),n.sessionId&&(i.sessionId=n.sessionId),n.integrations&&(i.integrations=n.integrations),i}const ft="_cnv_sid";function dt(t){if(!t)return null;const n=t.lastIndexOf(".");if(n<0)return null;const e=t.slice(0,n),r=Number(t.slice(n+1));return!ot(e)||!Number.isFinite(r)||r<=0?null:{id:e,lastActivity:r}}function ht(t={}){const n=Date.now(),e=(t=>{var n;return 6e4*(null!==(n=t.timeoutMinutes)&&void 0!==n?n:30)})(t),r=dt($(ft)),o=dt((t=>{if(!B())return null;try{return sessionStorage.getItem(t)}catch(t){return null}})(ft));let i=null;for(const t of[r,o])t&&(n-t.lastActivity>e||(!i||t.lastActivity>i.lastActivity)&&(i=t));const c=i?{id:i.id,lastActivity:n}:{id:et(),lastActivity:n};return yt(c,t),c.id}function yt(t,n={}){var e;const r=(t=>`${t.id}.${t.lastActivity}`)(t),o=null!==(e=n.timeoutMinutes)&&void 0!==e?e:30;L(ft,r,{domain:n.cookieDomain,maxAgeDays:o/1440,sameSite:"Lax"}),((t,n)=>{if(!B())return!1;try{return sessionStorage.setItem(t,n),!0}catch(t){return!1}})(ft,r)}class vt{constructor(t){var n,e;Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"storageKey",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"maxMessages",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"flushing",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"onlineHandler",{enumerable:!0,configurable:!0,writable:!0,value:null}),this.storageKey=null!==(n=t.storageKey)&&void 0!==n?n:"_cnv_rq",this.maxMessages=null!==(e=t.maxMessages)&&void 0!==e?e:100,this.installOnlineListener()}stash(t){if(t&&0!==t.length)try{const n=this.read();for(n.push(t);n.length>1&&pt(n)>this.maxMessages;)n.shift();this.write(n),this.config.debug&&console.log("[cnv-track] retry queued",t.length,"buffered",pt(n))}catch(t){}}flush(){if(this.flushing||"undefined"!=typeof navigator&&!1===navigator.onLine)return Promise.resolve();const t=this.read();if(0===t.length)return Promise.resolve();this.flushing=!0,this.write([]);const n=()=>{this.flushing=!1};return t.reduce((t,n)=>t.then(()=>this.config.resend(n)),Promise.resolve()).then(n,n)}size(){return pt(this.read())}clear(){try{E(this.storageKey)}catch(t){}}destroy(){if(this.onlineHandler){try{window.removeEventListener("online",this.onlineHandler)}catch(t){}this.onlineHandler=null}}installOnlineListener(){if("undefined"==typeof window)return;const t=()=>{this.flush()};try{window.addEventListener("online",t),this.onlineHandler=t}catch(t){}}read(){const t=q(this.storageKey);if(!t)return[];try{const n=JSON.parse(t);return Array.isArray(n)?n.filter(t=>Array.isArray(t)&&t.length>0):[]}catch(t){return[]}}write(t){0!==t.length?z(this.storageKey,JSON.stringify(t)):E(this.storageKey)}}function pt(t){let n=0;for(const e of t)n+=e.length;return n}class mt{constructor(t){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"buffer",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"timer",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"destroyed",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"handlers",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"retryQueue",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.retryQueue=new vt({debug:t.debug,resend:t=>this.send(t)}),this.installPageLifecycleHandlers(),this.retryQueue.flush()}setConfig(t){this.config=Object.assign(Object.assign({},this.config),t)}enqueue(t){this.destroyed||(this.buffer.push(t),this.buffer.length>=this.config.flushAt?this.flush():this.timer||(this.timer=setTimeout(()=>{this.timer=null,this.flush()},this.config.flushInterval)))}flush(){if(this.timer&&(clearTimeout(this.timer),this.timer=null),0===this.buffer.length)return Promise.resolve();const t=this.buffer;return this.buffer=[],this.send(t)}bufferedCount(){return this.buffer.length}destroy(){this.destroyed||(this.destroyed=!0,this.timer&&(clearTimeout(this.timer),this.timer=null),this.uninstallPageLifecycleHandlers(),this.retryQueue.destroy())}send(t){try{return this.config.useBatch?this.sendBatch(t):Promise.all(t.map(t=>this.sendSingle(t))).then(()=>{})}catch(t){return this.config.debug&&console.warn("[cnv-track] send failed synchronously; dropped batch",t),Promise.resolve()}}sendBatch(t){var n;const e=st(),r={batch:t.map(t=>bt(t,e)),context:Object.assign(Object.assign({},null!==(n=this.config.batchContext)&&void 0!==n?n:{}),{projectId:this.config.projectId,sentAt:e}),sentAt:e};return this.post(this.batchUrl(),r,t)}sendSingle(t){const n=bt(t,st());return this.post(this.singleUrl(t.type),n,[t])}batchUrl(){return this.config.endpoint+this.config.batchPath+"?projectId="+encodeURIComponent(this.config.projectId)}singleUrl(t){const n=this.config.singlePath.replace("{type}",t);return this.config.endpoint+n+"?projectId="+encodeURIComponent(this.config.projectId)}post(t,n,e){const r=JSON.stringify(n),o=((t,n)=>{try{return new Blob([t],{type:n})}catch(t){return null}})(r,this.config.contentType),i=o?o.size:(t=>{try{return new Blob([t]).size}catch(n){return t.length}})(r),c=i>65536?null:e;return c&&"undefined"!=typeof navigator&&!1===navigator.onLine?(this.retryQueue.stash(c),this.config.debug&&console.log("[cnv-track] offline; queued for retry",t),Promise.resolve()):o&&this.sendViaBeacon(t,o)?(this.config.debug&&console.log("[cnv-track] beacon",t),Promise.resolve()):this.sendViaFetch(t,r,c)}sendViaBeacon(t,n){if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;if(n.size>65536)return!1;try{return navigator.sendBeacon(t,n)}catch(t){return!1}}sendViaFetch(t,n,e){return"function"!=typeof fetch?(this.config.debug&&console.warn("[cnv-track] no transport available; queued for retry"),e&&this.retryQueue.stash(e),Promise.resolve()):fetch(t,{method:"POST",headers:{"Content-Type":this.config.contentType},body:n,keepalive:!0,credentials:"omit",mode:"cors"}).then(n=>{this.config.debug&&console.log("[cnv-track] fetch flushed",t,n.status)}).catch(n=>{this.config.debug&&console.warn("[cnv-track] flush failed; queued for retry",t,n),e&&this.retryQueue.stash(e)})}installPageLifecycleHandlers(){if("undefined"==typeof window||"undefined"==typeof document)return;const t=()=>{this.flush()},n=()=>{"hidden"===document.visibilityState&&this.flush()};try{window.addEventListener("pagehide",t),document.addEventListener("visibilitychange",n),this.handlers={pagehide:t,visibility:n}}catch(t){}}uninstallPageLifecycleHandlers(){if(this.handlers){try{window.removeEventListener("pagehide",this.handlers.pagehide),document.removeEventListener("visibilitychange",this.handlers.visibility)}catch(t){}this.handlers=null}}}function bt(t,n){return Object.assign(Object.assign({},t),{sentAt:n})}const wt="_cnv_uid",gt="_cnv_traits";function xt(){return $(wt)||q(wt)}const jt={endpoint:"https://tracking.cnvcdp.com/js/v1",batchPath:"/batch",singlePath:"/{type}",useBatch:!0,contentType:"text/plain",debug:!1,allowAdTracking:!0,cookieMaxAgeDays:730,sessionTimeoutMinutes:30,flushAt:20,flushInterval:5e3,autoPageview:!0,autotrack:!1,integrations:{},remoteConfig:W(void 0)},kt={analytics:!0,marketing:!0};function Ot(t,n){if(!Array.isArray(n)||0===n.length)return;const e=n;try{switch(e[0]){case"init":t.init(e[1],e[2]);break;case"pageview":t.pageview(e[1],e[2]);break;case"page":t.page(e[1],e[2]);break;case"screen":t.screen(e[1],e[2]);break;case"track":t.track(e[1],e[2]);break;case"identify":t.identify(e[1],e[2]);break;case"alias":t.alias(e[1],e[2]);break;case"group":t.group(e[1],e[2]);break;case"reset":t.reset();break;case"flush":t.flush();break;case"consent":t.consent(e[1]);break;case"set":t.set(e[1])}}catch(t){}}const St=/^(https:|zalo:)/i;class At extends Error{constructor(t){super(`loginWithZalo() thất bại: ${t}`),Object.defineProperty(this,"reason",{enumerable:!0,configurable:!0,writable:!0,value:t}),this.name="ZaloQrLoginError"}}function Tt(t,n,e,r){return`${t}${n}?${new URLSearchParams(Object.assign({projectId:e},r)).toString()}`}function It(t,n){return C(this,void 0,void 0,function*(){var e,r,o;if(!t.projectId||!t.endpoint)throw new At("not-initialized");if(!t.zaloQrLoginEnabled)throw new At("disabled");if(!t.consentGranted)throw new At("consent-required");const i={preferSse:null===(e=null==n?void 0:n.preferSse)||void 0===e||e,pollIntervalMs:null!==(r=null==n?void 0:n.pollIntervalMs)&&void 0!==r?r:2e3,autoIdentify:null===(o=null==n?void 0:n.autoIdentify)||void 0===o||o},c=t.projectId,u=t.endpoint,l=yield function(t,n){return C(this,void 0,void 0,function*(){const e="undefined"!=typeof AbortController?new AbortController:null,r=e?setTimeout(()=>e.abort(),8e3):null;let o;try{o=yield fetch(Tt(t,"/zalo-qr/session",n),{method:"POST",credentials:"omit",mode:"cors",signal:e?e.signal:void 0})}catch(t){throw new At("network")}finally{r&&clearTimeout(r)}if(!o.ok)throw new At(401===(i=o.status)?"invalid-project-key":403===i?"project-suspended":"network");var i;return(t=>{const n=t;if(!n||"string"!=typeof n.sessionToken||!n.sessionToken||"string"!=typeof n.miniAppDeepLink||!St.test(n.miniAppDeepLink)||"number"!=typeof n.expiresInSeconds||!Number.isFinite(n.expiresInSeconds)||n.expiresInSeconds<=0)throw new At("network");return{sessionToken:n.sessionToken,miniAppDeepLink:n.miniAppDeepLink,expiresInSeconds:n.expiresInSeconds}})(yield o.json())})}(u,c),s=((t,n,e,r,o)=>{let i=!1,c=null,u=null,l=null,s=null,a=null;const f=()=>{c&&(c.close(),c=null),null!==u&&(clearInterval(u),u=null),null!==l&&(clearTimeout(l),l=null),null!==s&&(clearTimeout(s),s=null)};return{promise:new Promise((d,h)=>{a=h;const y=t=>{i||(i=!0,f(),t())},v=t=>{"success"===t.status?y(()=>d((t=>({contactId:t.contactId,fullName:t.fullName,level:t.level,xp:t.xp}))(t))):"denied"===t.status?y(()=>h(new At("denied"))):"expired"===t.status&&y(()=>h(new At("expired")))},p=()=>{null!==u||i||(u=setInterval(()=>{fetch(Tt(t,"/zalo-qr/status",n,{sessionToken:e}),{method:"GET",credentials:"omit",mode:"cors"}).then(t=>404===t.status?v({status:"expired"}):t.ok?t.json().then(t=>v(t)):void 0).catch(()=>{})},o.pollIntervalMs))};if(o.preferSse&&"undefined"!=typeof EventSource)try{c=new EventSource(Tt(t,"/zalo-qr/stream",n,{sessionToken:e})),c.addEventListener("success",t=>{try{v(JSON.parse(t.data))}catch(t){}}),c.addEventListener("denied",()=>v({status:"denied"})),c.onerror=()=>{null!==s||i||(s=setTimeout(()=>{s=null,i||(c&&(c.close(),c=null),p())},3e3))}}catch(t){p()}else p();l=setTimeout(()=>{y(()=>h(new At("expired")))},1e3*r)}),cancel(){i||(i=!0,f(),null==a||a(new At("cancelled")))}}})(u,c,l.sessionToken,l.expiresInSeconds,i),a=s.promise.then(n=>{try{if(i.autoIdentify&&null!=n.contactId){const e=String(n.contactId),r=t.getUserId();null!=r&&r!==e?t.alias(e,r):t.identify(e,Object.assign(Object.assign(Object.assign({},n.fullName?{name:n.fullName}:{}),n.level?{level:n.level}:{}),null!=n.xp?{xp:n.xp}:{}))}}catch(t){}return n});return{sessionToken:l.sessionToken,qrPayload:l.miniAppDeepLink,expiresInSeconds:l.expiresInSeconds,result:a,cancel:s.cancel,retry(){return s.cancel(),It(t,n)}}})}const _t="0.2.0-beta.7",Pt="@cnv-vn/track",Mt=function(){let t=null,i=Object.assign({},jt),c=Object.assign({},kt),u=null,l=!1,s=null,a=null;function f(t){const n=Object.assign({},i);return t?(void 0!==t.endpoint&&(n.endpoint=(e=t.endpoint).endsWith("/")?e.slice(0,-1):e),void 0!==t.batchPath&&(n.batchPath=t.batchPath),void 0!==t.singlePath&&(n.singlePath=t.singlePath),void 0!==t.useBatch&&(n.useBatch=t.useBatch),void 0!==t.contentType&&(n.contentType=t.contentType),void 0!==t.debug&&(n.debug=t.debug),void 0!==t.allowAdTracking&&(n.allowAdTracking=t.allowAdTracking),void 0!==t.cookieDomain&&(n.cookieDomain=t.cookieDomain),void 0!==t.cookieMaxAgeDays&&(n.cookieMaxAgeDays=t.cookieMaxAgeDays),void 0!==t.sessionTimeoutMinutes&&(n.sessionTimeoutMinutes=t.sessionTimeoutMinutes),void 0!==t.flushAt&&(n.flushAt=t.flushAt),void 0!==t.flushInterval&&(n.flushInterval=t.flushInterval),void 0!==t.autoPageview&&(n.autoPageview=t.autoPageview),void 0!==t.autotrack&&(n.autotrack=t.autotrack),void 0!==t.integrations&&(n.integrations=t.integrations),void 0!==t.remoteConfig&&(n.remoteConfig=W(t.remoteConfig,i.remoteConfig)),n):n;var e}function d(){return u||(u=new mt({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,projectId:null!=t?t:"",debug:i.debug}),u)}function h(){var n;if(!t)return null;const e=((t={})=>{var n;const e={page:X(),screen:{width:K(()=>window.screen.width,0),height:K(()=>window.screen.height,0),density:K(()=>window.devicePixelRatio||1,1)},locale:K(()=>navigator.language||"",""),timezone:K(()=>Intl.DateTimeFormat().resolvedOptions().timeZone||"",""),userAgent:K(()=>navigator.userAgent||"",""),library:Y(t.library)},r=((t,n=!0)=>{const e=K(()=>window.location.search,"");if(!e)return;let r;try{r=new URLSearchParams(e)}catch(t){return}const o={};let i=!1;for(const[t,n]of H){const e=r.get(t);e&&e.length>0&&(o[n]=e,i=!0)}if(n){const t={};for(const n of V){const e=r.get(n);e&&e.length>0&&(t[n]=e)}Object.keys(t).length>0&&(o.clickIds=t,i=!0)}return i?o:void 0})(0,null===(n=t.allowAdTracking)||void 0===n||n);return r&&(e.campaign=r),e})({allowAdTracking:i.allowAdTracking});return c.analytics&&c.marketing||(e.consent=Object.assign({},c)),{anonymousId:ct({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),sessionId:ht({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),userId:null!==(n=xt())&&void 0!==n?n:void 0,context:e,projectId:t,integrations:Object.keys(i.integrations).length>0?i.integrations:void 0}}function y(t){i.debug&&console.warn(`[cnv-track] ${t}() called before init()`)}function v(t,n){try{if("string"!=typeof t||0===t.length)return;const e=h();if(!e)return y("track");const r=((t,n,e)=>{const r=at("track",e);return r.event=t,r.properties=null!=n?n:{},r})(t,n,e);d().enqueue(r)}catch(t){}}return{get initialized(){return l},get projectId(){return t},get endpoint(){return i.endpoint},isZaloQrLoginEnabled:()=>!!t&&(t=>{const n=G(t);return!n||!1!==(null===(r=null==(e=n.envelope)?void 0:e.zaloQrLogin)||void 0===r?void 0:r.enabled);var e,r})(t),get consentGranted(){return c.analytics&&c.marketing},init(h,y){try{if(!h||"string"!=typeof h)return;if(i=f(y),(null==y?void 0:y.consent)&&(c=Object.assign(Object.assign({},c),y.consent)),t=h,u&&u.destroy(),u=null,d(),l=!0,ct({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),ht({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),s){try{s()}catch(t){}s=null}const p={page(t,n){},track(t,n){return v(t,n)}};if(i.autotrack){let t=i.autotrack;i.remoteConfig.enabled&&"object"==typeof t&&t.buttonTracking&&(t=Object.assign(Object.assign({},t),{buttonTracking:void 0})),s=function(t,i){const c=void 0===(u=i)||!1===u?Object.assign(Object.assign({},D),{searchParams:P}):!0===u?Object.assign(Object.assign({},M),{searchParams:P}):{spa:null===(l=u.spa)||void 0===l||l,outboundClicks:null===(s=u.outboundClicks)||void 0===s||s,scroll:null===(a=u.scroll)||void 0===a||a,downloads:null===(f=u.downloads)||void 0===f||f,siteSearch:null===(d=u.siteSearch)||void 0===d||d,downloadExtensions:u.downloadExtensions,searchParams:null!==(h=u.searchParams)&&void 0!==h?h:P,buttonTracking:u.buttonTracking};var u,l,s,a,f,d,h;c.outboundClicks=!1,c.downloads=!1,c.scroll=!1,c.siteSearch=!1;const y=[];let v=null;function p(n){if(c.siteSearch)try{const e=null!=n?n:(()=>{try{return"undefined"!=typeof location?location.href:""}catch(t){return""}})();if(e===v)return;v=e;const r=((t,n=P)=>{try{if(!t){if("undefined"==typeof location)return null;t=location.href}const e=t instanceof URL?t:new URL(t,(()=>{try{return"undefined"!=typeof location?location.href:"http://localhost/"}catch(t){return"http://localhost/"}})());if(!e.search)return null;for(const t of n){const n=e.searchParams.get(t);if(null===n)continue;const r=n.trim();if(0!==r.length)return{query:r,param:t}}return null}catch(t){return null}})(e,c.searchParams);r&&U(()=>t.track("Site Searched",{query:r.query,param:r.param}))}catch(t){}}if(c.outboundClicks||c.downloads){const i=(t=>{var i;if("undefined"==typeof document)return()=>{};const c=new Set((null!==(i=t.downloadExtensions)&&void 0!==i?i:n).map(t=>t.toLowerCase())),u=!!t.onOutbound,l=!!t.onDownload;if(!u&&!l)return()=>{};function s(n){var i,s;try{if(0!==n.button)return;if(n.defaultPrevented)return;const a=(t=>{const n="function"==typeof t.composedPath?t.composedPath():[];if(n.length>0)for(const t of n)if(e(t))return t;let r=t.target;for(;r;){if(e(r))return r;r=r.parentNode}return null})(n);if(!a)return;const f=a.getAttribute("href");if(!f)return;if(/^(javascript:|mailto:|tel:|sms:)/i.test(f))return;if(f.startsWith("#"))return;const d=(t=>{try{return new URL(t,location.href)}catch(t){return null}})(f);if(!d)return;let h=!1;if(l){const n=a.hasAttribute("download"),e=(t=>{const n=r(t),e=n.lastIndexOf(".");return e<=0||e===n.length-1?null:n.slice(e+1).toLowerCase()})(d.pathname);(n||e&&c.has(e))&&(h=!0,null===(i=t.onDownload)||void 0===i||i.call(t,{url:d.href,fileName:r(d.pathname),fileExtension:null!=e?e:"",text:o(a)}))}u&&!h&&d.host!==(()=>{try{return"undefined"!=typeof location?location.hostname:""}catch(t){return""}})()&&d.host&&(null===(s=t.onOutbound)||void 0===s||s.call(t,{url:d.href,host:d.host,text:o(a),target:a.target||"_self"}))}catch(t){}}try{document.addEventListener("click",s,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",s,!0)}catch(t){}}})({downloadExtensions:c.downloadExtensions,onOutbound:c.outboundClicks?n=>U(()=>t.track("Clicked",n)):void 0,onDownload:c.downloads?n=>U(()=>t.track("File Downloaded",n)):void 0});y.push(i)}c.buttonTracking&&c.buttonTracking.rules&&c.buttonTracking.rules.length>0&&y.push(g(t,c.buttonTracking));let m=null;if(c.scroll&&(m=(t=>{if("undefined"==typeof window||"undefined"==typeof document)return{destroy(){},reset(){}};let n=!1,e=null,r=!1;function o(){if(e=null,!r&&!n)try{const e=document.documentElement,r=document.body,o=Math.max(e?e.scrollHeight:0,r?r.scrollHeight:0),i=Math.max(e?e.clientHeight:0,window.innerHeight||0);if(o<=i)return n=!0,c(),void t(90);((window.scrollY||(e?e.scrollTop:0)||0)+i)/o>=.9&&(n=!0,c(),t(90))}catch(t){}}function i(){if(null===e)try{e=window.requestAnimationFrame(o)}catch(t){o()}}function c(){try{window.removeEventListener("scroll",i),null!==e&&window.cancelAnimationFrame(e),e=null}catch(t){}}try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}return{destroy(){r=!0,c()},reset(){if(!r){n=!1;try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}}}}})(n=>{U(()=>t.track("Scrolled",{percent:n}))}),y.push(()=>null==m?void 0:m.destroy())),c.spa){const n=[],e=function(){if("undefined"==typeof window||"undefined"==typeof history)return()=>{};let e=_();function r(){try{const r=_();if(r===e)return;e=r,(e=>{const r=setTimeout(()=>{const e=n.indexOf(r);-1!==e&&n.splice(e,1),U(()=>t.page())},0);n.push(r),null==m||m.reset(),p(e)})(r)}catch(t){}}const o=history.pushState,i=history.replaceState,c=!0===o[I],u=!0===i[I];if(!c){function a(...t){o.apply(this,t),r()}a[I]=!0,history.pushState=a}if(!u){function f(...t){i.apply(this,t),r()}f[I]=!0,history.replaceState=f}const l=()=>r(),s=()=>r();try{window.addEventListener("popstate",l),window.addEventListener("hashchange",s)}catch(d){}return()=>{try{window.removeEventListener("popstate",l),window.removeEventListener("hashchange",s)}catch(t){}try{!c&&history.pushState[I]&&(history.pushState=o),!u&&history.replaceState[I]&&(history.replaceState=i)}catch(t){}}}();y.push(()=>{for(let t=0;t<n.length;t++)clearTimeout(n[t]);n.length=0,e()})}return p(),()=>{for(;y.length>0;){const t=y.pop();try{null==t||t()}catch(t){}}v=null}}(p,t)}if(a){try{a()}catch(t){}a=null}if(i.remoteConfig.enabled&&t){const n=t=>{if(a){try{a()}catch(t){}a=null}t&&t.rules&&t.rules.length>0&&(a=g(p,t))},e=(t=>{const n=G(t);return n?Q(n.envelope):null})(t);e&&n(e),function(t){return C(this,void 0,void 0,function*(){if(!t.projectId)return{status:"error"};const n=G(t.projectId);if(n&&Date.now()-n.fetchedAt<t.ttlMs)return{status:"unchanged"};const e=yield function(t){return C(this,void 0,void 0,function*(){var n;if("function"!=typeof fetch)return null;const e=t.endpoint+t.path+"?projectId="+encodeURIComponent(t.projectId),r="undefined"!=typeof AbortController?new AbortController:null,o=r?setTimeout(()=>r.abort(),t.timeoutMs):null;try{const o={};t.etag&&(o["If-None-Match"]=t.etag);const i=yield fetch(e,{method:"GET",headers:o,credentials:"omit",mode:"cors",signal:r?r.signal:void 0});if(304===i.status)return{envelope:null,etag:null!==(n=t.etag)&&void 0!==n?n:"",notModified:!0};if(!i.ok)return null;const c=i.headers&&"function"==typeof i.headers.get?i.headers.get("ETag"):null;return{envelope:yield i.json(),etag:null!=c?c:"",notModified:!1}}catch(t){return null}finally{o&&clearTimeout(o)}})}({endpoint:t.endpoint,path:t.path,projectId:t.projectId,timeoutMs:t.timeoutMs,etag:null==n?void 0:n.etag});return e?e.notModified?(n&&F(t.projectId,n.etag,n.envelope),{status:"unchanged"}):e.envelope?(F(t.projectId,e.etag,e.envelope),{status:"updated",buttonTracking:Q(e.envelope)}):{status:"error"}:{status:"error"}})}({endpoint:i.endpoint,path:i.remoteConfig.path,projectId:t,timeoutMs:i.remoteConfig.timeoutMs,ttlMs:i.remoteConfig.ttlMs}).then(t=>{"updated"===t.status&&n(t.buttonTracking)}).catch(()=>{})}}catch(t){}},pageview(t,n){},page(t,n){},screen(t,n){},track:v,identify(t,n){try{if("string"!=typeof t||0===t.length)return;((t,n,e={})=>{var r,o;if(t&&(L(wt,t,{domain:e.cookieDomain,maxAgeDays:null!==(r=e.cookieMaxAgeDays)&&void 0!==r?r:730,sameSite:"Lax"}),z(wt,t),null!=n)){const t=null!==(o=(()=>{const t=q(gt);if(!t)return null;try{const n=JSON.parse(t);return n&&"object"==typeof n&&!Array.isArray(n)?n:null}catch(t){return null}})())&&void 0!==o?o:{},e=Object.assign(Object.assign({},t),n);try{z(gt,JSON.stringify(e))}catch(t){}}})(t,n,{cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays});const e=h();if(!e)return y("identify");const r=((t,n,e)=>{var r;const o=at("identify",e);return t&&(o.userId=t),o.traits=null!==(r=n)&&void 0!==r?r:{},o})(t,n,e);d().enqueue(r)}catch(t){}},alias(t,n){},group(t,n){},reset(){try{((t={})=>{((t,n={})=>{var e;if(!N)return!1;try{let r=`${encodeURIComponent(t)}=`;return r+=`; path=${null!==(e=n.path)&&void 0!==e?e:"/"}`,n.domain&&(r+=`; domain=${n.domain}`),r+="; max-age=0",r+="; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=r,null===$(t)}catch(t){return!1}})(wt,{domain:t.cookieDomain}),E(wt),E(gt)})({cookieDomain:i.cookieDomain}),((t={})=>{ut(et(),t)})({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),((t={})=>{yt({id:et(),lastActivity:Date.now()},t)})({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes})}catch(t){}},flush(){try{u&&u.flush()}catch(t){}},consent(t){try{c=Object.assign(Object.assign({},c),t)}catch(t){}},set(t){try{i=f(t),t.consent&&(c=Object.assign(Object.assign({},c),t.consent)),u&&u.setConfig({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,debug:i.debug})}catch(t){}},destroy(){if(s){try{s()}catch(t){}s=null}if(a){try{a()}catch(t){}a=null}u&&u.destroy(),u=null,l=!1,t=null,i=Object.assign({},jt),c=Object.assign({},kt)}}}();function Dt(t){return It({projectId:Mt.projectId,endpoint:Mt.endpoint,zaloQrLoginEnabled:Mt.isZaloQrLoginEnabled(),consentGranted:Mt.consentGranted,identify:Mt.identify,alias:Mt.alias,getUserId:xt},t)}"undefined"!=typeof window&&(t=>{if("undefined"==typeof window)return()=>{};const n=window.cnvQ;if(n&&n.__loaded)return()=>{};const e=window.cnvQ=null!=n?n:[],r=e.splice(0,e.length);e.push.bind(e),e.push=(...n)=>{for(const e of n)Ot(t,e);return e.length},e.__loaded=!0;for(const n of r)Ot(t,n)})(Mt);const Ut=Object.assign(Mt,{version:_t,name:Pt,schemaVersion:lt,loginWithZalo:Dt});t.NAME=Pt,t.SCHEMA_VERSION=lt,t.VERSION=_t,t.ZaloQrLoginError=At,t.default=Ut,t.loginWithZalo=Dt,Object.defineProperty(t,"t",{value:!0})}(this.cnvTrack=this.cnvTrack||{});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @cnv-vn/track v0.2.0-beta.7
|
|
3
|
+
* (c) 2026 CNV
|
|
4
|
+
* Released under the UNLICENSED license.
|
|
5
|
+
*/
|
|
6
|
+
var t,n;t=this,n=function(t){const n=["pdf","doc","docx","xls","xlsx","ppt","pptx","txt","csv","rtf","zip","rar","7z","tar","gz","mp3","mp4","mov","wav","avi","mkv","dmg","exe","pkg","apk","msi","svg"];function e(t){return!!t&&"object"==typeof t&&1===t.nodeType&&"A"===t.tagName}function r(t){const n=t.lastIndexOf("/");return-1===n?t:t.slice(n+1)}function o(t){var n,e,r;let o=(null!==(n=t.textContent)&&void 0!==n?n:"").trim();return o||(o=(null!==(e=t.getAttribute("aria-label"))&&void 0!==e?e:"").trim()),o||(o=(null!==(r=t.getAttribute("title"))&&void 0!==r?r:"").trim()),o.length>200?o.slice(0,200):o}const i=200;function c(t){let n="";try{n=(t.textContent||"").replace(/\s+/g," ").trim(),n||(n=(t.getAttribute("aria-label")||"").trim()),n||(n=(t.getAttribute("title")||"").trim()),n=n.normalize("NFC")}catch(t){return""}return n.length>i?n.slice(0,i):n}function u(t){const n=!1!==t.caseInsensitive;if("regex"===t.mode)return{mode:"regex",needle:t.value,ci:n,re:b(t.value,n)};const e=t.value.normalize("NFC");return{mode:t.mode,needle:n?e.toLowerCase():e,ci:n,re:null}}function l(t,n){try{return t.matches(n)}catch(t){return!1}}function s(t){try{if("A"===t.tagName){const n=t.getAttribute("href");return n?m(n):null}const n=t.getAttribute("formaction");if(n)return m(n);const e=t.getAttribute("data-href");if(e)return m(e);const r=t.closest("form");if(r){const t=r.getAttribute("action");if(t)return m(t)}return null}catch(t){return null}}function a(t){return"regex"===t.mode?{mode:"regex",needle:t.value,re:b(t.value,!0)}:{mode:t.mode,needle:t.value.toLowerCase(),re:null}}function f(t){if("startsWith"===t.mode)return{mode:"startsWith",needle:t.value,re:null};const n="glob"===t.mode?"^"+t.value.replace(/[.+^${}()|[\]\\?]/g,"\\$&").replace(/\*/g,".*")+"$":t.value;return{mode:t.mode,needle:t.value,re:b(n,!1)}}function d(t,n){return"startsWith"===t.mode?0===n.indexOf(t.needle):!t.re||g(t.re,n)}const h=/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,y=/\+?\d[\d\s().-]{7,}\d/g;function v(t){if(!t)return t;try{return t.replace(h,"[email]").replace(y,"[phone]")}catch(n){return t}}function p(t){return!!t&&"object"==typeof t&&1===t.nodeType}function m(t){try{return new URL(t,location.href).href}catch(n){return t}}function b(t,n){try{return!t||t.length>1e3?null:new RegExp(t,n?"i":"")}catch(t){return null}}function g(t,n){try{return t.test(n)}catch(t){return!1}}function w(t,n){if("undefined"==typeof document)return()=>{};const e=(t=>{const n=t.globalThrottle||{},e=[],r=t.rules||[];for(let t=0;t<r.length;t++){const o=r[t];if(!o||!1===o.enabled)continue;if(!o.match||!o.event)continue;const i=o.match,c=o.throttle||{};e.push({rule:o,elementSpec:x(o),selector:i.selector||null,text:i.text?u(i.text):null,dest:i.destinationUrl?a(i.destinationUrl):null,urlScope:o.urlScope?f(o.urlScope):null,dedupeWindowMs:T(c.dedupeWindowMs,T(n.dedupeWindowMs,500)),maxPerPageview:T(c.maxPerPageview,T(n.maxPerPageview,1/0)),samplingRate:A(T(c.samplingRate,T(n.samplingRate,1)))})}return e.sort((t,n)=>(t.rule.priority||0)-(n.rule.priority||0)),e})(n);if(0===e.length)return()=>{};const r=new WeakMap;let o=0;const i=t=>{let n=r.get(t);return void 0===n&&(n=++o,r.set(t,n)),String(n)},c=new Map,l=new Map;let s=S();function h(n){try{if(0!==n.button)return;const r=S();r!==s&&(s=r,l.clear(),c.clear());const o=(t=>{const n=[];try{const e="function"==typeof t.composedPath?t.composedPath():[];if(e&&e.length>0){for(const t of e)p(t)&&n.push(t);return n}let r=t.target;for(;r;)p(r)&&n.push(r),r=r.parentNode}catch(t){}return n})(n);if(0===o.length)return;const u=(()=>{try{return"undefined"!=typeof location?location.pathname+location.search:""}catch(t){return""}})();for(let r=0;r<e.length;r++){const s=e[r];if(s.urlScope&&!d(s.urlScope,u))continue;let a=null;for(let t=0;t<o.length;t++)if(j(s,o[t])){a=o[t];break}if(!a)continue;const f=s.rule.id;if((l.get(f)||0)>=s.maxPerPageview)return;const h=f+"#"+i(a),y=Date.now(),v=c.get(h);if(void 0!==v&&y-v<s.dedupeWindowMs)return;if(s.samplingRate<1&&Math.random()>=s.samplingRate)return;c.set(h,y),l.set(f,(l.get(f)||0)+1);const p=k(s.rule,a,n);return void O(()=>t.track("button_click",p))}}catch(t){}}try{document.addEventListener("click",h,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",h,!0)}catch(t){}c.clear(),l.clear()}}function x(t){const n=t.match;return n.element?n.element:n.selector||n.id||n.dataAttr||n.classes&&n.classes.length>0||n.destinationUrl?"any":void 0}function j(t,n){if(!((t,n)=>{if(!n)return(t=>{const n=t.tagName;if("BUTTON"===n||"A"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")})(t);if("any"===n)return!0;if("button"===n){const n=t.tagName;if("BUTTON"===n)return!0;if("INPUT"===n){const n=(t.getAttribute("type")||"").toLowerCase();return"submit"===n||"button"===n||"reset"===n}return"button"===t.getAttribute("role")}return"link"===n?"A"===t.tagName:l(t,n)})(n,t.elementSpec))return!1;const e=t.rule.match;return!(t.selector&&!l(n,t.selector)||void 0!==e.id&&!((t,n)=>t.id===n)(n,e.id)||e.classes&&e.classes.length>0&&!((t,n)=>{try{for(let e=0;e<n.length;e++)if(!t.classList.contains(n[e]))return!1;return!0}catch(t){return!1}})(n,e.classes)||e.dataAttr&&!((t,n,e)=>{try{return!!t.hasAttribute(n)&&(void 0===e||t.getAttribute(n)===e)}catch(t){return!1}})(n,e.dataAttr.name,e.dataAttr.value)||t.text&&!((t,n)=>{const e=c(t);if("regex"===n.mode)return!!n.re&&g(n.re,e);const r=n.ci?e.toLowerCase():e;return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.text)||t.dest&&!((t,n)=>{const e=s(t);if(!e)return!1;if("regex"===n.mode)return!!n.re&&g(n.re,e);if("domain"===n.mode)try{return new URL(e).host.toLowerCase()===n.needle}catch(t){return!1}const r=e.toLowerCase();return"exact"===n.mode?r===n.needle:"startsWith"===n.mode?0===r.indexOf(n.needle):-1!==r.indexOf(n.needle)})(n,t.dest))}function k(t,n,e){const r={},o=t.event.staticProps;if(o)for(const t of Object.keys(o))r[t]=o[t];const u=t.capture;if(u)try{if(u.text){const t=v(c(n));t&&(r.text=t)}if(u.href){const t=s(n);t&&(r.href=t)}if(u.elementId&&n.id&&(r.element_id=n.id),u.classes&&(r.element_classes=Array.from(n.classList)),u.dataAttrs&&u.dataAttrs.length>0)for(let t=0;t<u.dataAttrs.length;t++){const e=u.dataAttrs[t],o=n.getAttribute(e);null!==o&&(r[e]=v(o))}if(u.valueFrom){const t=((t,n)=>{try{const e=t.querySelector(n.selector)||(t.parentElement?t.parentElement.querySelector(n.selector):null);if(!e)return null;const r=n.attr?e.getAttribute(n.attr):e.textContent||"";if(null===r)return null;const o=v(r.replace(/\s+/g," ").trim());return o.length>i?o.slice(0,i):o}catch(t){return null}})(n,u.valueFrom);null!==t&&(r[u.valueFrom.as]=t)}u.selectorPath&&(r.selector_path=((t,n=5)=>{try{const e=[];let r=t,o=0;for(;r&&1===r.nodeType&&o<n;){let t=r.tagName.toLowerCase();if(r.id){e.unshift(t+"#"+r.id);break}const n=Array.from(r.classList).slice(0,2).join(".");n&&(t+="."+n),e.unshift(t),r=r.parentElement,o++}return e.join(" > ")}catch(t){return""}})(n)),u.position&&(r.x=e.clientX,r.y=e.clientY)}catch(t){}return r.rule_id=t.id,t.name&&(r.rule_name=t.name),t.event&&t.event.name&&(r.event_name=t.event.name),r.tag=n.tagName.toLowerCase(),r}function O(t){try{t()}catch(t){}}function S(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}function T(t,n){return"number"!=typeof t||isNaN(t)?n:t}function A(t){return t<0?0:t>1?1:t}const I="__cnvPatched";function _(){try{return"undefined"!=typeof location?location.href:""}catch(t){return""}}const P=["q","s","query","search","keyword"],M={spa:!0,outboundClicks:!0,scroll:!0,downloads:!0,siteSearch:!0},D={spa:!1,outboundClicks:!1,scroll:!1,downloads:!1,siteSearch:!1};function U(t){try{t()}catch(t){}}function C(t,n,e,r){return new(e||(e=Promise))((o,i)=>{function c(t){try{l(r.next(t))}catch(t){i(t)}}function u(t){try{l(r.throw(t))}catch(t){i(t)}}function l(t){var n;t.done?o(t.value):(n=t.value,n instanceof e?n:new e(t=>{t(n)})).then(c,u)}l((r=r.apply(t,n||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;const N="undefined"!=typeof window&&"undefined"!=typeof document;function $(t){if(!N)return null;try{const n=document.cookie;if(!n)return null;const e=encodeURIComponent(t),r=n.split(";");for(let n=0;n<r.length;n++){const o=r[n].indexOf("=");if(o<0)continue;const i=r[n].slice(0,o).trim();if(i===e||i===t)try{return decodeURIComponent(r[n].slice(o+1))}catch(t){return r[n].slice(o+1)}}return null}catch(t){return null}}function L(t,n,e={}){var r,o,i;if(!N)return!1;try{let c=`${encodeURIComponent(t)}=${encodeURIComponent(n)}`;if(c+=`; path=${null!==(r=e.path)&&void 0!==r?r:"/"}`,e.domain&&(c+=`; domain=${e.domain}`),"number"==typeof e.maxAgeDays){const t=Math.floor(86400*e.maxAgeDays);c+=`; max-age=${t}`,c+=`; expires=${new Date(Date.now()+1e3*t).toUTCString()}`}return c+=`; SameSite=${null!==(o=e.sameSite)&&void 0!==o?o:"Lax"}`,(null!==(i=e.secure)&&void 0!==i?i:"undefined"!=typeof location&&"https:"===location.protocol)&&(c+="; Secure"),document.cookie=c,$(t)===n}catch(t){return!1}}function R(){if(!N)return!1;try{const t="__cnv_probe__";return localStorage.setItem(t,"1"),localStorage.removeItem(t),!0}catch(t){return!1}}function q(t){if(!R())return null;try{return localStorage.getItem(t)}catch(t){return null}}function z(t,n){if(!R())return!1;try{return localStorage.setItem(t,n),!0}catch(t){return!1}}function E(t){if(!R())return!1;try{return localStorage.removeItem(t),!0}catch(t){return!1}}function B(){if(!N)return!1;try{const t="__cnv_probe__";return sessionStorage.setItem(t,"1"),sessionStorage.removeItem(t),!0}catch(t){return!1}}const J={path:"/config",timeoutMs:4e3,ttlMs:3e5};function W(t,n){var e,r;const o=null!=n?n:Object.assign({enabled:!0},J);return void 0===t?Object.assign({},o):!0===t?Object.assign(Object.assign({},o),{enabled:!0}):!1===t?Object.assign(Object.assign({},o),{enabled:!1}):{enabled:null===(e=t.enabled)||void 0===e||e,path:null!==(r=t.path)&&void 0!==r?r:o.path,timeoutMs:"number"==typeof t.timeoutMs?t.timeoutMs:o.timeoutMs,ttlMs:"number"==typeof t.ttlMs?t.ttlMs:o.ttlMs}}function Z(t){return"_cnv_cfg_"+t}function G(t){const n=q(Z(t));if(!n)return null;try{const t=JSON.parse(n);return t&&"object"==typeof t&&t.envelope?t:null}catch(t){return null}}function F(t,n,e){try{const r={etag:n,envelope:e,fetchedAt:Date.now()};z(Z(t),JSON.stringify(r))}catch(t){}}function Q(t){const n=null==t?void 0:t.buttonTracking;if(!n||!1===n.enabled)return null;const e=Array.isArray(n.rules)?n.rules:[];return 0===e.length?null:{rules:e,globalThrottle:n.globalThrottle}}const V=["gclid","fbclid","ttclid","msclkid","dclid"],H=[["utm_source","source"],["utm_medium","medium"],["utm_campaign","name"],["utm_term","term"],["utm_content","content"],["utm_id","id"]];function K(t,n){try{return t()}catch(t){return n}}function X(){const t=K(()=>window.location.href,"");let n="",e="",r="";if(t)try{const o=new URL(t);n=o.pathname,e=o.search,r=o.hash}catch(t){}return{url:t,path:n,search:e,hash:r,title:K(()=>document.title,""),referrer:K(()=>document.referrer,"")}}function Y(t){var n,e;let r="@cnv-vn/track",o="0.0.0-dev";try{r="@cnv-vn/track"}catch(t){}try{o="0.2.0-beta.7"}catch(t){}return{name:null!==(n=null==t?void 0:t.name)&&void 0!==n?n:r,version:null!==(e=null==t?void 0:t.version)&&void 0!==e?e:o}}const tt="0123456789abcdef";function nt(t){return tt[t>>>4&15]+tt[15&t]}function et(){if("undefined"!=typeof crypto){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();if("function"==typeof crypto.getRandomValues){const t=new Uint8Array(16);return crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128,nt(t[0])+nt(t[1])+nt(t[2])+nt(t[3])+"-"+nt(t[4])+nt(t[5])+"-"+nt(t[6])+nt(t[7])+"-"+nt(t[8])+nt(t[9])+"-"+nt(t[10])+nt(t[11])+nt(t[12])+nt(t[13])+nt(t[14])+nt(t[15])}}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const n=16*Math.random()|0;return("x"===t?n:3&n|8).toString(16)})}const rt=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function ot(t){return"string"==typeof t&&rt.test(t)}const it="_cnv_cid";function ct(t={}){const n=$(it),e=q(it);let r=null;return ot(n)?r=n:ot(e)&&(r=e),r||(r=et()),ut(r,t),r}function ut(t,n={}){var e;L(it,t,{domain:n.cookieDomain,maxAgeDays:null!==(e=n.cookieMaxAgeDays)&&void 0!==e?e:730,sameSite:"Lax"}),z(it,t)}const lt="1.0";function st(){return(new Date).toISOString()}function at(t,n){var e;const r=null!==(e=n.timestamp)&&void 0!==e?e:st(),o=Object.assign(Object.assign({},n.context),{projectId:n.projectId,schemaVersion:lt}),i={type:t,messageId:et(),anonymousId:n.anonymousId,timestamp:r,originalTimestamp:r,context:o};return n.userId&&(i.userId=n.userId),n.sessionId&&(i.sessionId=n.sessionId),n.integrations&&(i.integrations=n.integrations),i}const ft="_cnv_sid";function dt(t){if(!t)return null;const n=t.lastIndexOf(".");if(n<0)return null;const e=t.slice(0,n),r=Number(t.slice(n+1));return!ot(e)||!Number.isFinite(r)||r<=0?null:{id:e,lastActivity:r}}function ht(t={}){const n=Date.now(),e=(t=>{var n;return 6e4*(null!==(n=t.timeoutMinutes)&&void 0!==n?n:30)})(t),r=dt($(ft)),o=dt((t=>{if(!B())return null;try{return sessionStorage.getItem(t)}catch(t){return null}})(ft));let i=null;for(const t of[r,o])t&&(n-t.lastActivity>e||(!i||t.lastActivity>i.lastActivity)&&(i=t));const c=i?{id:i.id,lastActivity:n}:{id:et(),lastActivity:n};return yt(c,t),c.id}function yt(t,n={}){var e;const r=(t=>`${t.id}.${t.lastActivity}`)(t),o=null!==(e=n.timeoutMinutes)&&void 0!==e?e:30;L(ft,r,{domain:n.cookieDomain,maxAgeDays:o/1440,sameSite:"Lax"}),((t,n)=>{if(!B())return!1;try{return sessionStorage.setItem(t,n),!0}catch(t){return!1}})(ft,r)}class vt{constructor(t){var n,e;Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"storageKey",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"maxMessages",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"flushing",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"onlineHandler",{enumerable:!0,configurable:!0,writable:!0,value:null}),this.storageKey=null!==(n=t.storageKey)&&void 0!==n?n:"_cnv_rq",this.maxMessages=null!==(e=t.maxMessages)&&void 0!==e?e:100,this.installOnlineListener()}stash(t){if(t&&0!==t.length)try{const n=this.read();for(n.push(t);n.length>1&&pt(n)>this.maxMessages;)n.shift();this.write(n),this.config.debug&&console.log("[cnv-track] retry queued",t.length,"buffered",pt(n))}catch(t){}}flush(){if(this.flushing||"undefined"!=typeof navigator&&!1===navigator.onLine)return Promise.resolve();const t=this.read();if(0===t.length)return Promise.resolve();this.flushing=!0,this.write([]);const n=()=>{this.flushing=!1};return t.reduce((t,n)=>t.then(()=>this.config.resend(n)),Promise.resolve()).then(n,n)}size(){return pt(this.read())}clear(){try{E(this.storageKey)}catch(t){}}destroy(){if(this.onlineHandler){try{window.removeEventListener("online",this.onlineHandler)}catch(t){}this.onlineHandler=null}}installOnlineListener(){if("undefined"==typeof window)return;const t=()=>{this.flush()};try{window.addEventListener("online",t),this.onlineHandler=t}catch(t){}}read(){const t=q(this.storageKey);if(!t)return[];try{const n=JSON.parse(t);return Array.isArray(n)?n.filter(t=>Array.isArray(t)&&t.length>0):[]}catch(t){return[]}}write(t){0!==t.length?z(this.storageKey,JSON.stringify(t)):E(this.storageKey)}}function pt(t){let n=0;for(const e of t)n+=e.length;return n}class mt{constructor(t){Object.defineProperty(this,"config",{enumerable:!0,configurable:!0,writable:!0,value:t}),Object.defineProperty(this,"buffer",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"timer",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"destroyed",{enumerable:!0,configurable:!0,writable:!0,value:!1}),Object.defineProperty(this,"handlers",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"retryQueue",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.retryQueue=new vt({debug:t.debug,resend:t=>this.send(t)}),this.installPageLifecycleHandlers(),this.retryQueue.flush()}setConfig(t){this.config=Object.assign(Object.assign({},this.config),t)}enqueue(t){this.destroyed||(this.buffer.push(t),this.buffer.length>=this.config.flushAt?this.flush():this.timer||(this.timer=setTimeout(()=>{this.timer=null,this.flush()},this.config.flushInterval)))}flush(){if(this.timer&&(clearTimeout(this.timer),this.timer=null),0===this.buffer.length)return Promise.resolve();const t=this.buffer;return this.buffer=[],this.send(t)}bufferedCount(){return this.buffer.length}destroy(){this.destroyed||(this.destroyed=!0,this.timer&&(clearTimeout(this.timer),this.timer=null),this.uninstallPageLifecycleHandlers(),this.retryQueue.destroy())}send(t){try{return this.config.useBatch?this.sendBatch(t):Promise.all(t.map(t=>this.sendSingle(t))).then(()=>{})}catch(t){return this.config.debug&&console.warn("[cnv-track] send failed synchronously; dropped batch",t),Promise.resolve()}}sendBatch(t){var n;const e=st(),r={batch:t.map(t=>bt(t,e)),context:Object.assign(Object.assign({},null!==(n=this.config.batchContext)&&void 0!==n?n:{}),{projectId:this.config.projectId,sentAt:e}),sentAt:e};return this.post(this.batchUrl(),r,t)}sendSingle(t){const n=bt(t,st());return this.post(this.singleUrl(t.type),n,[t])}batchUrl(){return this.config.endpoint+this.config.batchPath+"?projectId="+encodeURIComponent(this.config.projectId)}singleUrl(t){const n=this.config.singlePath.replace("{type}",t);return this.config.endpoint+n+"?projectId="+encodeURIComponent(this.config.projectId)}post(t,n,e){const r=JSON.stringify(n),o=((t,n)=>{try{return new Blob([t],{type:n})}catch(t){return null}})(r,this.config.contentType),i=o?o.size:(t=>{try{return new Blob([t]).size}catch(n){return t.length}})(r),c=i>65536?null:e;return c&&"undefined"!=typeof navigator&&!1===navigator.onLine?(this.retryQueue.stash(c),this.config.debug&&console.log("[cnv-track] offline; queued for retry",t),Promise.resolve()):o&&this.sendViaBeacon(t,o)?(this.config.debug&&console.log("[cnv-track] beacon",t),Promise.resolve()):this.sendViaFetch(t,r,c)}sendViaBeacon(t,n){if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;if(n.size>65536)return!1;try{return navigator.sendBeacon(t,n)}catch(t){return!1}}sendViaFetch(t,n,e){return"function"!=typeof fetch?(this.config.debug&&console.warn("[cnv-track] no transport available; queued for retry"),e&&this.retryQueue.stash(e),Promise.resolve()):fetch(t,{method:"POST",headers:{"Content-Type":this.config.contentType},body:n,keepalive:!0,credentials:"omit",mode:"cors"}).then(n=>{this.config.debug&&console.log("[cnv-track] fetch flushed",t,n.status)}).catch(n=>{this.config.debug&&console.warn("[cnv-track] flush failed; queued for retry",t,n),e&&this.retryQueue.stash(e)})}installPageLifecycleHandlers(){if("undefined"==typeof window||"undefined"==typeof document)return;const t=()=>{this.flush()},n=()=>{"hidden"===document.visibilityState&&this.flush()};try{window.addEventListener("pagehide",t),document.addEventListener("visibilitychange",n),this.handlers={pagehide:t,visibility:n}}catch(t){}}uninstallPageLifecycleHandlers(){if(this.handlers){try{window.removeEventListener("pagehide",this.handlers.pagehide),document.removeEventListener("visibilitychange",this.handlers.visibility)}catch(t){}this.handlers=null}}}function bt(t,n){return Object.assign(Object.assign({},t),{sentAt:n})}const gt="_cnv_uid",wt="_cnv_traits";function xt(){return $(gt)||q(gt)}const jt={endpoint:"https://tracking.cnvcdp.com/js/v1",batchPath:"/batch",singlePath:"/{type}",useBatch:!0,contentType:"text/plain",debug:!1,allowAdTracking:!0,cookieMaxAgeDays:730,sessionTimeoutMinutes:30,flushAt:20,flushInterval:5e3,autoPageview:!0,autotrack:!1,integrations:{},remoteConfig:W(void 0)},kt={analytics:!0,marketing:!0};function Ot(t,n){if(!Array.isArray(n)||0===n.length)return;const e=n;try{switch(e[0]){case"init":t.init(e[1],e[2]);break;case"pageview":t.pageview(e[1],e[2]);break;case"page":t.page(e[1],e[2]);break;case"screen":t.screen(e[1],e[2]);break;case"track":t.track(e[1],e[2]);break;case"identify":t.identify(e[1],e[2]);break;case"alias":t.alias(e[1],e[2]);break;case"group":t.group(e[1],e[2]);break;case"reset":t.reset();break;case"flush":t.flush();break;case"consent":t.consent(e[1]);break;case"set":t.set(e[1])}}catch(t){}}const St=/^(https:|zalo:)/i;class Tt extends Error{constructor(t){super(`loginWithZalo() thất bại: ${t}`),Object.defineProperty(this,"reason",{enumerable:!0,configurable:!0,writable:!0,value:t}),this.name="ZaloQrLoginError"}}function At(t,n,e,r){return`${t}${n}?${new URLSearchParams(Object.assign({projectId:e},r)).toString()}`}function It(t,n){return C(this,void 0,void 0,function*(){var e,r,o;if(!t.projectId||!t.endpoint)throw new Tt("not-initialized");if(!t.zaloQrLoginEnabled)throw new Tt("disabled");if(!t.consentGranted)throw new Tt("consent-required");const i={preferSse:null===(e=null==n?void 0:n.preferSse)||void 0===e||e,pollIntervalMs:null!==(r=null==n?void 0:n.pollIntervalMs)&&void 0!==r?r:2e3,autoIdentify:null===(o=null==n?void 0:n.autoIdentify)||void 0===o||o},c=t.projectId,u=t.endpoint,l=yield function(t,n){return C(this,void 0,void 0,function*(){const e="undefined"!=typeof AbortController?new AbortController:null,r=e?setTimeout(()=>e.abort(),8e3):null;let o;try{o=yield fetch(At(t,"/zalo-qr/session",n),{method:"POST",credentials:"omit",mode:"cors",signal:e?e.signal:void 0})}catch(t){throw new Tt("network")}finally{r&&clearTimeout(r)}if(!o.ok)throw new Tt(401===(i=o.status)?"invalid-project-key":403===i?"project-suspended":"network");var i;return(t=>{const n=t;if(!n||"string"!=typeof n.sessionToken||!n.sessionToken||"string"!=typeof n.miniAppDeepLink||!St.test(n.miniAppDeepLink)||"number"!=typeof n.expiresInSeconds||!Number.isFinite(n.expiresInSeconds)||n.expiresInSeconds<=0)throw new Tt("network");return{sessionToken:n.sessionToken,miniAppDeepLink:n.miniAppDeepLink,expiresInSeconds:n.expiresInSeconds}})(yield o.json())})}(u,c),s=((t,n,e,r,o)=>{let i=!1,c=null,u=null,l=null,s=null,a=null;const f=()=>{c&&(c.close(),c=null),null!==u&&(clearInterval(u),u=null),null!==l&&(clearTimeout(l),l=null),null!==s&&(clearTimeout(s),s=null)};return{promise:new Promise((d,h)=>{a=h;const y=t=>{i||(i=!0,f(),t())},v=t=>{"success"===t.status?y(()=>d((t=>({contactId:t.contactId,fullName:t.fullName,level:t.level,xp:t.xp}))(t))):"denied"===t.status?y(()=>h(new Tt("denied"))):"expired"===t.status&&y(()=>h(new Tt("expired")))},p=()=>{null!==u||i||(u=setInterval(()=>{fetch(At(t,"/zalo-qr/status",n,{sessionToken:e}),{method:"GET",credentials:"omit",mode:"cors"}).then(t=>404===t.status?v({status:"expired"}):t.ok?t.json().then(t=>v(t)):void 0).catch(()=>{})},o.pollIntervalMs))};if(o.preferSse&&"undefined"!=typeof EventSource)try{c=new EventSource(At(t,"/zalo-qr/stream",n,{sessionToken:e})),c.addEventListener("success",t=>{try{v(JSON.parse(t.data))}catch(t){}}),c.addEventListener("denied",()=>v({status:"denied"})),c.onerror=()=>{null!==s||i||(s=setTimeout(()=>{s=null,i||(c&&(c.close(),c=null),p())},3e3))}}catch(t){p()}else p();l=setTimeout(()=>{y(()=>h(new Tt("expired")))},1e3*r)}),cancel(){i||(i=!0,f(),null==a||a(new Tt("cancelled")))}}})(u,c,l.sessionToken,l.expiresInSeconds,i),a=s.promise.then(n=>{try{if(i.autoIdentify&&null!=n.contactId){const e=String(n.contactId),r=t.getUserId();null!=r&&r!==e?t.alias(e,r):t.identify(e,Object.assign(Object.assign(Object.assign({},n.fullName?{name:n.fullName}:{}),n.level?{level:n.level}:{}),null!=n.xp?{xp:n.xp}:{}))}}catch(t){}return n});return{sessionToken:l.sessionToken,qrPayload:l.miniAppDeepLink,expiresInSeconds:l.expiresInSeconds,result:a,cancel:s.cancel,retry:()=>(s.cancel(),It(t,n))}})}const _t="0.2.0-beta.7",Pt="@cnv-vn/track",Mt=function(){let t=null,i=Object.assign({},jt),c=Object.assign({},kt),u=null,l=!1,s=null,a=null;function f(t){const n=Object.assign({},i);return t?(void 0!==t.endpoint&&(n.endpoint=(e=t.endpoint).endsWith("/")?e.slice(0,-1):e),void 0!==t.batchPath&&(n.batchPath=t.batchPath),void 0!==t.singlePath&&(n.singlePath=t.singlePath),void 0!==t.useBatch&&(n.useBatch=t.useBatch),void 0!==t.contentType&&(n.contentType=t.contentType),void 0!==t.debug&&(n.debug=t.debug),void 0!==t.allowAdTracking&&(n.allowAdTracking=t.allowAdTracking),void 0!==t.cookieDomain&&(n.cookieDomain=t.cookieDomain),void 0!==t.cookieMaxAgeDays&&(n.cookieMaxAgeDays=t.cookieMaxAgeDays),void 0!==t.sessionTimeoutMinutes&&(n.sessionTimeoutMinutes=t.sessionTimeoutMinutes),void 0!==t.flushAt&&(n.flushAt=t.flushAt),void 0!==t.flushInterval&&(n.flushInterval=t.flushInterval),void 0!==t.autoPageview&&(n.autoPageview=t.autoPageview),void 0!==t.autotrack&&(n.autotrack=t.autotrack),void 0!==t.integrations&&(n.integrations=t.integrations),void 0!==t.remoteConfig&&(n.remoteConfig=W(t.remoteConfig,i.remoteConfig)),n):n;var e}function d(){return u||(u=new mt({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,projectId:null!=t?t:"",debug:i.debug}),u)}function h(){var n;if(!t)return null;const e=((t={})=>{var n;const e={page:X(),screen:{width:K(()=>window.screen.width,0),height:K(()=>window.screen.height,0),density:K(()=>window.devicePixelRatio||1,1)},locale:K(()=>navigator.language||"",""),timezone:K(()=>Intl.DateTimeFormat().resolvedOptions().timeZone||"",""),userAgent:K(()=>navigator.userAgent||"",""),library:Y(t.library)},r=((t,n=!0)=>{const e=K(()=>window.location.search,"");if(!e)return;let r;try{r=new URLSearchParams(e)}catch(t){return}const o={};let i=!1;for(const[t,n]of H){const e=r.get(t);e&&e.length>0&&(o[n]=e,i=!0)}if(n){const t={};for(const n of V){const e=r.get(n);e&&e.length>0&&(t[n]=e)}Object.keys(t).length>0&&(o.clickIds=t,i=!0)}return i?o:void 0})(0,null===(n=t.allowAdTracking)||void 0===n||n);return r&&(e.campaign=r),e})({allowAdTracking:i.allowAdTracking});return c.analytics&&c.marketing||(e.consent=Object.assign({},c)),{anonymousId:ct({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),sessionId:ht({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),userId:null!==(n=xt())&&void 0!==n?n:void 0,context:e,projectId:t,integrations:Object.keys(i.integrations).length>0?i.integrations:void 0}}function y(t){i.debug&&console.warn(`[cnv-track] ${t}() called before init()`)}function v(t,n){try{if("string"!=typeof t||0===t.length)return;const e=h();if(!e)return y("track");const r=((t,n,e)=>{const r=at("track",e);return r.event=t,r.properties=null!=n?n:{},r})(t,n,e);d().enqueue(r)}catch(t){}}return{get initialized(){return l},get projectId(){return t},get endpoint(){return i.endpoint},isZaloQrLoginEnabled(){return!!t&&(t=>{const n=G(t);return!n||!1!==(null===(r=null==(e=n.envelope)?void 0:e.zaloQrLogin)||void 0===r?void 0:r.enabled);var e,r})(t)},get consentGranted(){return c.analytics&&c.marketing},init(h,y){try{if(!h||"string"!=typeof h)return;if(i=f(y),(null==y?void 0:y.consent)&&(c=Object.assign(Object.assign({},c),y.consent)),t=h,u&&u.destroy(),u=null,d(),l=!0,ct({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),ht({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes}),s){try{s()}catch(t){}s=null}const p={page(t,n){},track:(t,n)=>v(t,n)};if(i.autotrack){let t=i.autotrack;i.remoteConfig.enabled&&"object"==typeof t&&t.buttonTracking&&(t=Object.assign(Object.assign({},t),{buttonTracking:void 0})),s=function(t,i){const c=void 0===(u=i)||!1===u?Object.assign(Object.assign({},D),{searchParams:P}):!0===u?Object.assign(Object.assign({},M),{searchParams:P}):{spa:null===(l=u.spa)||void 0===l||l,outboundClicks:null===(s=u.outboundClicks)||void 0===s||s,scroll:null===(a=u.scroll)||void 0===a||a,downloads:null===(f=u.downloads)||void 0===f||f,siteSearch:null===(d=u.siteSearch)||void 0===d||d,downloadExtensions:u.downloadExtensions,searchParams:null!==(h=u.searchParams)&&void 0!==h?h:P,buttonTracking:u.buttonTracking};var u,l,s,a,f,d,h;c.outboundClicks=!1,c.downloads=!1,c.scroll=!1,c.siteSearch=!1;const y=[];let v=null;function p(n){if(c.siteSearch)try{const e=null!=n?n:(()=>{try{return"undefined"!=typeof location?location.href:""}catch(t){return""}})();if(e===v)return;v=e;const r=((t,n=P)=>{try{if(!t){if("undefined"==typeof location)return null;t=location.href}const e=t instanceof URL?t:new URL(t,(()=>{try{return"undefined"!=typeof location?location.href:"http://localhost/"}catch(t){return"http://localhost/"}})());if(!e.search)return null;for(const t of n){const n=e.searchParams.get(t);if(null===n)continue;const r=n.trim();if(0!==r.length)return{query:r,param:t}}return null}catch(t){return null}})(e,c.searchParams);r&&U(()=>t.track("Site Searched",{query:r.query,param:r.param}))}catch(t){}}if(c.outboundClicks||c.downloads){const i=(t=>{var i;if("undefined"==typeof document)return()=>{};const c=new Set((null!==(i=t.downloadExtensions)&&void 0!==i?i:n).map(t=>t.toLowerCase())),u=!!t.onOutbound,l=!!t.onDownload;if(!u&&!l)return()=>{};function s(n){var i,s;try{if(0!==n.button)return;if(n.defaultPrevented)return;const a=(t=>{const n="function"==typeof t.composedPath?t.composedPath():[];if(n.length>0)for(const t of n)if(e(t))return t;let r=t.target;for(;r;){if(e(r))return r;r=r.parentNode}return null})(n);if(!a)return;const f=a.getAttribute("href");if(!f)return;if(/^(javascript:|mailto:|tel:|sms:)/i.test(f))return;if(f.startsWith("#"))return;const d=(t=>{try{return new URL(t,location.href)}catch(t){return null}})(f);if(!d)return;let h=!1;if(l){const n=a.hasAttribute("download"),e=(t=>{const n=r(t),e=n.lastIndexOf(".");return e<=0||e===n.length-1?null:n.slice(e+1).toLowerCase()})(d.pathname);(n||e&&c.has(e))&&(h=!0,null===(i=t.onDownload)||void 0===i||i.call(t,{url:d.href,fileName:r(d.pathname),fileExtension:null!=e?e:"",text:o(a)}))}u&&!h&&d.host!==(()=>{try{return"undefined"!=typeof location?location.hostname:""}catch(t){return""}})()&&d.host&&(null===(s=t.onOutbound)||void 0===s||s.call(t,{url:d.href,host:d.host,text:o(a),target:a.target||"_self"}))}catch(t){}}try{document.addEventListener("click",s,!0)}catch(t){return()=>{}}return()=>{try{document.removeEventListener("click",s,!0)}catch(t){}}})({downloadExtensions:c.downloadExtensions,onOutbound:c.outboundClicks?n=>U(()=>t.track("Clicked",n)):void 0,onDownload:c.downloads?n=>U(()=>t.track("File Downloaded",n)):void 0});y.push(i)}c.buttonTracking&&c.buttonTracking.rules&&c.buttonTracking.rules.length>0&&y.push(w(t,c.buttonTracking));let m=null;if(c.scroll&&(m=(t=>{if("undefined"==typeof window||"undefined"==typeof document)return{destroy(){},reset(){}};let n=!1,e=null,r=!1;function o(){if(e=null,!r&&!n)try{const e=document.documentElement,r=document.body,o=Math.max(e?e.scrollHeight:0,r?r.scrollHeight:0),i=Math.max(e?e.clientHeight:0,window.innerHeight||0);if(o<=i)return n=!0,c(),void t(90);((window.scrollY||(e?e.scrollTop:0)||0)+i)/o>=.9&&(n=!0,c(),t(90))}catch(t){}}function i(){if(null===e)try{e=window.requestAnimationFrame(o)}catch(t){o()}}function c(){try{window.removeEventListener("scroll",i),null!==e&&window.cancelAnimationFrame(e),e=null}catch(t){}}try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}return{destroy(){r=!0,c()},reset(){if(!r){n=!1;try{window.addEventListener("scroll",i,{passive:!0})}catch(t){try{window.addEventListener("scroll",i)}catch(t){}}try{window.requestAnimationFrame(o)}catch(t){o()}}}}})(n=>{U(()=>t.track("Scrolled",{percent:n}))}),y.push(()=>null==m?void 0:m.destroy())),c.spa){const n=[],e=function(){if("undefined"==typeof window||"undefined"==typeof history)return()=>{};let e=_();function r(){try{const r=_();if(r===e)return;e=r,(e=>{const r=setTimeout(()=>{const e=n.indexOf(r);-1!==e&&n.splice(e,1),U(()=>t.page())},0);n.push(r),null==m||m.reset(),p(e)})(r)}catch(t){}}const o=history.pushState,i=history.replaceState,c=!0===o[I],u=!0===i[I];if(!c){function a(...t){o.apply(this,t),r()}a[I]=!0,history.pushState=a}if(!u){function f(...t){i.apply(this,t),r()}f[I]=!0,history.replaceState=f}const l=()=>r(),s=()=>r();try{window.addEventListener("popstate",l),window.addEventListener("hashchange",s)}catch(d){}return()=>{try{window.removeEventListener("popstate",l),window.removeEventListener("hashchange",s)}catch(t){}try{!c&&history.pushState[I]&&(history.pushState=o),!u&&history.replaceState[I]&&(history.replaceState=i)}catch(t){}}}();y.push(()=>{for(let t=0;t<n.length;t++)clearTimeout(n[t]);n.length=0,e()})}return p(),()=>{for(;y.length>0;){const t=y.pop();try{null==t||t()}catch(t){}}v=null}}(p,t)}if(a){try{a()}catch(t){}a=null}if(i.remoteConfig.enabled&&t){const n=t=>{if(a){try{a()}catch(t){}a=null}t&&t.rules&&t.rules.length>0&&(a=w(p,t))},e=(t=>{const n=G(t);return n?Q(n.envelope):null})(t);e&&n(e),function(t){return C(this,void 0,void 0,function*(){if(!t.projectId)return{status:"error"};const n=G(t.projectId);if(n&&Date.now()-n.fetchedAt<t.ttlMs)return{status:"unchanged"};const e=yield function(t){return C(this,void 0,void 0,function*(){var n;if("function"!=typeof fetch)return null;const e=t.endpoint+t.path+"?projectId="+encodeURIComponent(t.projectId),r="undefined"!=typeof AbortController?new AbortController:null,o=r?setTimeout(()=>r.abort(),t.timeoutMs):null;try{const o={};t.etag&&(o["If-None-Match"]=t.etag);const i=yield fetch(e,{method:"GET",headers:o,credentials:"omit",mode:"cors",signal:r?r.signal:void 0});if(304===i.status)return{envelope:null,etag:null!==(n=t.etag)&&void 0!==n?n:"",notModified:!0};if(!i.ok)return null;const c=i.headers&&"function"==typeof i.headers.get?i.headers.get("ETag"):null;return{envelope:yield i.json(),etag:null!=c?c:"",notModified:!1}}catch(t){return null}finally{o&&clearTimeout(o)}})}({endpoint:t.endpoint,path:t.path,projectId:t.projectId,timeoutMs:t.timeoutMs,etag:null==n?void 0:n.etag});return e?e.notModified?(n&&F(t.projectId,n.etag,n.envelope),{status:"unchanged"}):e.envelope?(F(t.projectId,e.etag,e.envelope),{status:"updated",buttonTracking:Q(e.envelope)}):{status:"error"}:{status:"error"}})}({endpoint:i.endpoint,path:i.remoteConfig.path,projectId:t,timeoutMs:i.remoteConfig.timeoutMs,ttlMs:i.remoteConfig.ttlMs}).then(t=>{"updated"===t.status&&n(t.buttonTracking)}).catch(()=>{})}}catch(t){}},pageview(t,n){},page(t,n){},screen(t,n){},track:v,identify(t,n){try{if("string"!=typeof t||0===t.length)return;((t,n,e={})=>{var r,o;if(t&&(L(gt,t,{domain:e.cookieDomain,maxAgeDays:null!==(r=e.cookieMaxAgeDays)&&void 0!==r?r:730,sameSite:"Lax"}),z(gt,t),null!=n)){const t=null!==(o=(()=>{const t=q(wt);if(!t)return null;try{const n=JSON.parse(t);return n&&"object"==typeof n&&!Array.isArray(n)?n:null}catch(t){return null}})())&&void 0!==o?o:{},e=Object.assign(Object.assign({},t),n);try{z(wt,JSON.stringify(e))}catch(t){}}})(t,n,{cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays});const e=h();if(!e)return y("identify");const r=((t,n,e)=>{var r;const o=at("identify",e);return t&&(o.userId=t),o.traits=null!==(r=n)&&void 0!==r?r:{},o})(t,n,e);d().enqueue(r)}catch(t){}},alias(t,n){},group(t,n){},reset(){try{((t={})=>{((t,n={})=>{var e;if(!N)return!1;try{let r=`${encodeURIComponent(t)}=`;return r+=`; path=${null!==(e=n.path)&&void 0!==e?e:"/"}`,n.domain&&(r+=`; domain=${n.domain}`),r+="; max-age=0",r+="; expires=Thu, 01 Jan 1970 00:00:00 GMT",document.cookie=r,null===$(t)}catch(t){return!1}})(gt,{domain:t.cookieDomain}),E(gt),E(wt)})({cookieDomain:i.cookieDomain}),((t={})=>{ut(et(),t)})({cookieDomain:i.cookieDomain,cookieMaxAgeDays:i.cookieMaxAgeDays}),((t={})=>{yt({id:et(),lastActivity:Date.now()},t)})({cookieDomain:i.cookieDomain,timeoutMinutes:i.sessionTimeoutMinutes})}catch(t){}},flush(){try{u&&u.flush()}catch(t){}},consent(t){try{c=Object.assign(Object.assign({},c),t)}catch(t){}},set(t){try{i=f(t),t.consent&&(c=Object.assign(Object.assign({},c),t.consent)),u&&u.setConfig({endpoint:i.endpoint,batchPath:i.batchPath,singlePath:i.singlePath,useBatch:i.useBatch,contentType:i.contentType,flushAt:i.flushAt,flushInterval:i.flushInterval,debug:i.debug})}catch(t){}},destroy(){if(s){try{s()}catch(t){}s=null}if(a){try{a()}catch(t){}a=null}u&&u.destroy(),u=null,l=!1,t=null,i=Object.assign({},jt),c=Object.assign({},kt)}}}();function Dt(t){return It({projectId:Mt.projectId,endpoint:Mt.endpoint,zaloQrLoginEnabled:Mt.isZaloQrLoginEnabled(),consentGranted:Mt.consentGranted,identify:Mt.identify,alias:Mt.alias,getUserId:xt},t)}"undefined"!=typeof window&&(t=>{if("undefined"==typeof window)return()=>{};const n=window.cnvQ;if(n&&n.__loaded)return()=>{};const e=window.cnvQ=null!=n?n:[],r=e.splice(0,e.length);e.push.bind(e),e.push=(...n)=>{for(const e of n)Ot(t,e);return e.length},e.__loaded=!0;for(const n of r)Ot(t,n)})(Mt);const Ut=Object.assign(Mt,{version:_t,name:Pt,schemaVersion:lt,loginWithZalo:Dt});t.NAME=Pt,t.SCHEMA_VERSION=lt,t.VERSION=_t,t.ZaloQrLoginError=Tt,t.default=Ut,t.loginWithZalo=Dt,Object.defineProperty(t,"t",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).cnvTrack={});
|
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* cnv-track.js — Data Schema (Segment Spec compatible)
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Wire-format contract with the CNV Tracking Gateway. Mirrors the Segment
|
|
7
|
+
* Spec common envelope (camelCase keys) so payloads decode 1:1 into the
|
|
8
|
+
* server-side `SegmentMessage` / `SegmentBatchRequest` DTOs.
|
|
9
|
+
*
|
|
10
|
+
* Endpoints (relative to `SDKOptions.endpoint`, default `https://tracking.cnvcdp.com/js/v1`):
|
|
11
|
+
*
|
|
12
|
+
* POST /segment/batch ← N messages per request (default flush)
|
|
13
|
+
* POST /segment/{type} ← single-message mode (debug / unbatched)
|
|
14
|
+
* where {type} ∈ track | identify | page | screen | group | alias
|
|
15
|
+
*
|
|
16
|
+
* Where `projectId` lives:
|
|
17
|
+
* - URL query param `?projectId=…` (routing / auth at the edge)
|
|
18
|
+
* - `BatchRequest.context.projectId` (so backend can read off the body too)
|
|
19
|
+
* - `SegmentMessage.context.projectId` (per-message, in case messages get
|
|
20
|
+
* forwarded individually downstream)
|
|
21
|
+
*
|
|
22
|
+
* Compatibility policy:
|
|
23
|
+
* - Additive changes (new optional fields) → minor `SCHEMA_VERSION` bump.
|
|
24
|
+
* - Renames / removals / type changes → bump major.
|
|
25
|
+
* ============================================================================
|
|
26
|
+
*/
|
|
27
|
+
/** Wire-format schema version. Stamped into `context.schemaVersion` per message. */
|
|
28
|
+
declare const SCHEMA_VERSION: "1.0";
|
|
29
|
+
/** ISO-8601 timestamp string with millisecond precision and timezone offset. */
|
|
30
|
+
type ISOTimestamp = string;
|
|
31
|
+
/** RFC-4122 v4 UUID string. */
|
|
32
|
+
type UUID = string;
|
|
33
|
+
/**
|
|
34
|
+
* Request `Content-Type`. `text/plain` is CORS-safelisted, so the browser
|
|
35
|
+
* skips the OPTIONS preflight (and `sendBeacon` fires cleanly during unload);
|
|
36
|
+
* `application/json` is stricter on the wire but triggers a preflight. The
|
|
37
|
+
* JSON body itself is identical either way — only the header differs. See
|
|
38
|
+
* Sub-task 4.3.
|
|
39
|
+
*/
|
|
40
|
+
type ContentType = 'text/plain' | 'application/json';
|
|
41
|
+
/** JSON-serializable primitive. */
|
|
42
|
+
type JSONPrimitive = string | number | boolean | null;
|
|
43
|
+
/** Any JSON-serializable value. */
|
|
44
|
+
type JSONValue = JSONPrimitive | JSONValue[] | {
|
|
45
|
+
[key: string]: JSONValue;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The six message types the gateway accepts. Mirrors the regex in
|
|
49
|
+
* `@PostMapping("segment/{type:track|identify|page|screen|group|alias}")`.
|
|
50
|
+
*/
|
|
51
|
+
type MessageType = 'track' | 'identify' | 'page' | 'screen' | 'group' | 'alias';
|
|
52
|
+
interface ContextPage {
|
|
53
|
+
/** Full URL including query string and fragment. */
|
|
54
|
+
url: string;
|
|
55
|
+
/** Pathname only (e.g., `/products/42`). */
|
|
56
|
+
path: string;
|
|
57
|
+
/** Query string including the leading `?`. Empty string if none. */
|
|
58
|
+
search: string;
|
|
59
|
+
/** Hash fragment including the leading `#`. Empty string if none. */
|
|
60
|
+
hash: string;
|
|
61
|
+
/** `document.title` at the time the event was created. */
|
|
62
|
+
title: string;
|
|
63
|
+
/** `document.referrer` — may be empty for direct visits. */
|
|
64
|
+
referrer: string;
|
|
65
|
+
}
|
|
66
|
+
interface ContextScreen {
|
|
67
|
+
width: number;
|
|
68
|
+
height: number;
|
|
69
|
+
/** `window.devicePixelRatio` — useful for high-DPI segmentation. */
|
|
70
|
+
density: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Marketing attribution block. Field names follow the Segment Spec:
|
|
74
|
+
* utm_campaign → name utm_source → source utm_medium → medium
|
|
75
|
+
* utm_term → term utm_content → content
|
|
76
|
+
*
|
|
77
|
+
* `id` and `clickIds` are CNV extensions (no Segment equivalent) — Spring
|
|
78
|
+
* Jackson will route them through `SegmentMessage.extra` if a strict mapping
|
|
79
|
+
* exists, or via `Map<String, Object>` if `context` stays loosely typed.
|
|
80
|
+
*/
|
|
81
|
+
interface ContextCampaign {
|
|
82
|
+
/** `utm_campaign` — Segment maps to `campaign.name`. */
|
|
83
|
+
name?: string;
|
|
84
|
+
/** `utm_source` */
|
|
85
|
+
source?: string;
|
|
86
|
+
/** `utm_medium` */
|
|
87
|
+
medium?: string;
|
|
88
|
+
/** `utm_term` */
|
|
89
|
+
term?: string;
|
|
90
|
+
/** `utm_content` */
|
|
91
|
+
content?: string;
|
|
92
|
+
/** `utm_id` — CNV extension. */
|
|
93
|
+
id?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Platform click IDs (gclid, fbclid, ttclid, msclkid, dclid, ...).
|
|
96
|
+
* CNV extension — collected verbatim from the query string.
|
|
97
|
+
*/
|
|
98
|
+
clickIds?: Record<string, string>;
|
|
99
|
+
}
|
|
100
|
+
interface ContextLibrary {
|
|
101
|
+
/** Always `@cnv/track`. */
|
|
102
|
+
name: string;
|
|
103
|
+
/** Semver string. Stamped at build time. */
|
|
104
|
+
version: string;
|
|
105
|
+
}
|
|
106
|
+
interface ContextConsent {
|
|
107
|
+
analytics: boolean;
|
|
108
|
+
marketing: boolean;
|
|
109
|
+
}
|
|
110
|
+
interface PayloadContext {
|
|
111
|
+
page?: ContextPage;
|
|
112
|
+
screen?: ContextScreen;
|
|
113
|
+
/** `navigator.language` (e.g., `vi-VN`). */
|
|
114
|
+
locale?: string;
|
|
115
|
+
/** IANA timezone (e.g., `Asia/Ho_Chi_Minh`). */
|
|
116
|
+
timezone?: string;
|
|
117
|
+
/** Verbatim `navigator.userAgent`. Gateway is expected to parse + drop. */
|
|
118
|
+
userAgent?: string;
|
|
119
|
+
library?: ContextLibrary;
|
|
120
|
+
campaign?: ContextCampaign;
|
|
121
|
+
/**
|
|
122
|
+
* Pre-consent flag. Gateway may downgrade storage or skip forwarding to
|
|
123
|
+
* marketing destinations when `marketing === false`.
|
|
124
|
+
*/
|
|
125
|
+
consent?: ContextConsent;
|
|
126
|
+
/**
|
|
127
|
+
* CNV extension — projectId is duplicated here so backend code that
|
|
128
|
+
* forwards individual messages still has access to the source project
|
|
129
|
+
* without having to thread the batch-level context through.
|
|
130
|
+
*/
|
|
131
|
+
projectId?: string;
|
|
132
|
+
/** SDK wire-format version. Echoed for forward-compat triage. */
|
|
133
|
+
schemaVersion?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* One event in the wire format.
|
|
137
|
+
*
|
|
138
|
+
* Field name and types deliberately mirror `com.cnv.tracking.dto.SegmentMessage`
|
|
139
|
+
* so Jackson decodes the payload into typed fields rather than the `extra`
|
|
140
|
+
* map. Any field NOT listed here will land in `extra` on the server side.
|
|
141
|
+
*
|
|
142
|
+
* type — Discriminator. Required.
|
|
143
|
+
* messageId — UUID v4 per event. Server uses for idempotent dedupe.
|
|
144
|
+
* anonymousId — Device-level id from cookie + localStorage.
|
|
145
|
+
* userId — Authenticated user id (only after `identify()`).
|
|
146
|
+
* sessionId — Rotates after 30' inactivity. Java accepts `session_id` alias.
|
|
147
|
+
* timestamp — When the event was created on the client.
|
|
148
|
+
* originalTimestamp — Stamped once on the client; never overridden during
|
|
149
|
+
* offline retries. Equal to `timestamp` on first send.
|
|
150
|
+
* sentAt — Stamped just before the request leaves the browser.
|
|
151
|
+
*
|
|
152
|
+
* event — Custom event name. ONLY for type=track.
|
|
153
|
+
* name — Page / screen name. For type=page or type=screen.
|
|
154
|
+
* category — Optional grouping. For type=page or type=screen.
|
|
155
|
+
* properties — For track / page / screen.
|
|
156
|
+
* traits — For identify / group.
|
|
157
|
+
*
|
|
158
|
+
* groupId — Required for type=group.
|
|
159
|
+
* previousId — Required for type=alias (the *old* id being aliased away).
|
|
160
|
+
*
|
|
161
|
+
* context — Auto-collected environment (see {@link PayloadContext}).
|
|
162
|
+
* integrations— Per-destination on/off toggles (Segment convention).
|
|
163
|
+
*/
|
|
164
|
+
interface SegmentMessage {
|
|
165
|
+
type: MessageType;
|
|
166
|
+
messageId: UUID;
|
|
167
|
+
anonymousId: UUID;
|
|
168
|
+
userId?: string;
|
|
169
|
+
sessionId?: string;
|
|
170
|
+
timestamp: ISOTimestamp;
|
|
171
|
+
originalTimestamp: ISOTimestamp;
|
|
172
|
+
sentAt?: ISOTimestamp;
|
|
173
|
+
event?: string;
|
|
174
|
+
name?: string;
|
|
175
|
+
category?: string;
|
|
176
|
+
properties?: Record<string, JSONValue>;
|
|
177
|
+
traits?: Record<string, JSONValue>;
|
|
178
|
+
context?: PayloadContext;
|
|
179
|
+
groupId?: string;
|
|
180
|
+
previousId?: string;
|
|
181
|
+
integrations?: Record<string, JSONValue>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Wrapper sent to `POST /segment/batch`. Shape matches
|
|
185
|
+
* `com.cnv.tracking.dto.SegmentBatchRequest`:
|
|
186
|
+
*
|
|
187
|
+
* batch — N SegmentMessage records.
|
|
188
|
+
* context — Batch-level context. We use it to hoist `projectId` once
|
|
189
|
+
* instead of repeating per message. (Per-message context
|
|
190
|
+
* still carries page / screen / campaign as those vary
|
|
191
|
+
* between events in the same batch — SPA navigation, etc.)
|
|
192
|
+
* integrations — Batch-level destination toggles.
|
|
193
|
+
*/
|
|
194
|
+
interface SegmentBatchRequest {
|
|
195
|
+
batch: SegmentMessage[];
|
|
196
|
+
context?: Record<string, JSONValue>;
|
|
197
|
+
integrations?: Record<string, JSONValue>;
|
|
198
|
+
sentAt?: ISOTimestamp;
|
|
199
|
+
}
|
|
200
|
+
/** Argument shape for `identify(userId, traits)`. */
|
|
201
|
+
interface UserTraits {
|
|
202
|
+
email?: string;
|
|
203
|
+
phone?: string;
|
|
204
|
+
name?: string;
|
|
205
|
+
firstName?: string;
|
|
206
|
+
lastName?: string;
|
|
207
|
+
createdAt?: ISOTimestamp;
|
|
208
|
+
[key: string]: JSONValue | undefined;
|
|
209
|
+
}
|
|
210
|
+
interface SDKOptions {
|
|
211
|
+
/** Tracking Gateway base URL. Default: `https://tracking.cnvcdp.com/js/v1`. */
|
|
212
|
+
endpoint?: string;
|
|
213
|
+
/** Path appended for batch flush. Default: `/segment/batch`. */
|
|
214
|
+
batchPath?: string;
|
|
215
|
+
/** Per-type single-message path template. `{type}` is interpolated. */
|
|
216
|
+
singlePath?: string;
|
|
217
|
+
/** Use batch endpoint (true, default) or one request per event. */
|
|
218
|
+
useBatch?: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Request `Content-Type`. Default `text/plain` — CORS-safelisted, so no
|
|
221
|
+
* OPTIONS preflight. Switch to `application/json` only if the gateway
|
|
222
|
+
* rejects text bodies (then it must allow the preflight). See Sub-task 4.3.
|
|
223
|
+
*/
|
|
224
|
+
contentType?: ContentType;
|
|
225
|
+
/** Verbose console output. */
|
|
226
|
+
debug?: boolean;
|
|
227
|
+
/** Disable marketing-related identifiers and click-id capture. */
|
|
228
|
+
allowAdTracking?: boolean;
|
|
229
|
+
/** Cookie domain (e.g., `.cnv.vn` for cross-subdomain stitching). */
|
|
230
|
+
cookieDomain?: string;
|
|
231
|
+
/** Cookie lifetime in days. Default 730 (≈ 2 years, ITP-safe upper bound). */
|
|
232
|
+
cookieMaxAgeDays?: number;
|
|
233
|
+
/** Session inactivity timeout in minutes. Default 30. */
|
|
234
|
+
sessionTimeoutMinutes?: number;
|
|
235
|
+
/** Buffered events before forced flush. Default 20. */
|
|
236
|
+
flushAt?: number;
|
|
237
|
+
/** Max ms between flushes. Default 5000. */
|
|
238
|
+
flushInterval?: number;
|
|
239
|
+
/** Fire a `pageview` automatically on `init()`. Default true. */
|
|
240
|
+
autoPageview?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Enhanced Measurement — opt-in automatic instrumentation. Mirrors the
|
|
243
|
+
* features GA4's gtag enables by default. Pass `true` to enable every
|
|
244
|
+
* feature with sensible defaults, `false` to disable, or a granular object
|
|
245
|
+
* to flip individual features. See {@link AutotrackOptions}.
|
|
246
|
+
*
|
|
247
|
+
* Default: `false` (off, for backward compatibility).
|
|
248
|
+
*/
|
|
249
|
+
autotrack?: boolean | AutotrackOptions;
|
|
250
|
+
/** Initial consent state. Default `{ analytics: true, marketing: true }`. */
|
|
251
|
+
consent?: Partial<ContextConsent>;
|
|
252
|
+
/** Per-call integrations override. */
|
|
253
|
+
integrations?: Record<string, JSONValue>;
|
|
254
|
+
/**
|
|
255
|
+
* Remote config — tự nạp button-tracking rules từ Tracking Gateway
|
|
256
|
+
* (`GET {endpoint}{path}?projectId=`) lúc `init`. Rule do dashboard CDP quản
|
|
257
|
+
* lý (single source of truth); SDK chỉ đọc + cache (localStorage + ETag) và
|
|
258
|
+
* fail-open. **Mặc định on** — SDK tự nạp rules mà không cần khai báo. Pass
|
|
259
|
+
* `false` để tắt, hoặc object để tinh chỉnh. Xem {@link RemoteConfigOptions}.
|
|
260
|
+
*/
|
|
261
|
+
remoteConfig?: boolean | RemoteConfigOptions;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Tùy chọn cho remote-config fetch. Nguồn rule là dashboard CDP — SDK chỉ tải
|
|
265
|
+
* bản đã publish qua `GET {endpoint}{path}?projectId=` rồi cache lại.
|
|
266
|
+
*/
|
|
267
|
+
interface RemoteConfigOptions {
|
|
268
|
+
/** Bật/tắt. Mặc định `true` khi truyền object. */
|
|
269
|
+
enabled?: boolean;
|
|
270
|
+
/** Path nối sau `endpoint` để lấy config. Mặc định `/config`. */
|
|
271
|
+
path?: string;
|
|
272
|
+
/** Timeout cho request fetch config (ms). Mặc định 4000. */
|
|
273
|
+
timeoutMs?: number;
|
|
274
|
+
/**
|
|
275
|
+
* Cửa sổ "còn tươi" của cache localStorage (ms): trong khoảng này SDK dùng
|
|
276
|
+
* thẳng cache, không gọi mạng. Mặc định 300000 (khớp `max-age=300` server).
|
|
277
|
+
*/
|
|
278
|
+
ttlMs?: number;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Granular toggles for the built-in autotrack instrumentation. Every feature
|
|
282
|
+
* defaults to `true` when `autotrack: true`, or can be individually flipped
|
|
283
|
+
* when the user passes an object.
|
|
284
|
+
*
|
|
285
|
+
* Event names follow Segment Spec conventions where one exists, with CNV
|
|
286
|
+
* extensions for the rest:
|
|
287
|
+
* - SPA pageview → emits `type:"page"` (same as manual `page()`)
|
|
288
|
+
* - Outbound link click → `track('Clicked', { url, … })`
|
|
289
|
+
* - 90% scroll depth → `track('Scrolled', { percent: 90 })`
|
|
290
|
+
* - File download click → `track('File Downloaded', { url, extension, … })`
|
|
291
|
+
* - Site search (?q= …) → `track('Site Searched', { query })`
|
|
292
|
+
*/
|
|
293
|
+
interface AutotrackOptions {
|
|
294
|
+
/**
|
|
295
|
+
* Monkey-patch `history.pushState` / `history.replaceState` and listen for
|
|
296
|
+
* `popstate` + `hashchange` so SPA route changes (React Router, Vue Router,
|
|
297
|
+
* Next.js App Router, …) fire a `page()` automatically. Default `true`.
|
|
298
|
+
*/
|
|
299
|
+
spa?: boolean;
|
|
300
|
+
/**
|
|
301
|
+
* Document-level click delegation. Fires `track('Clicked')`
|
|
302
|
+
* when the user clicks an `<a>` whose host differs from `location.host`.
|
|
303
|
+
* Default `true`.
|
|
304
|
+
*/
|
|
305
|
+
outboundClicks?: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* Passive scroll listener that fires `track('Scrolled', { percent: 90 })`
|
|
308
|
+
* exactly once per page when the user reaches ≥ 90% of the document height.
|
|
309
|
+
* Re-armed on SPA route change. Default `true`.
|
|
310
|
+
*/
|
|
311
|
+
scroll?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Same click delegation pipeline as `outboundClicks`. Fires
|
|
314
|
+
* `track('File Downloaded')` when an `<a href>` points at a recognized
|
|
315
|
+
* file extension (pdf, doc/docx, xls/xlsx, ppt/pptx, zip, rar, 7z, csv,
|
|
316
|
+
* txt, mp3, mp4, mov, wav, dmg, exe, pkg, gz). Default `true`.
|
|
317
|
+
*/
|
|
318
|
+
downloads?: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Inspect the URL query string for common search parameter names
|
|
321
|
+
* (`q`, `s`, `query`, `search`, `keyword`) and fire
|
|
322
|
+
* `track('Site Searched', { query })` whenever a non-empty value is found
|
|
323
|
+
* at pageview time. Default `true`.
|
|
324
|
+
*/
|
|
325
|
+
siteSearch?: boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Additional file extensions to treat as downloads (merged with defaults).
|
|
328
|
+
* Lower-case, no leading dot.
|
|
329
|
+
*/
|
|
330
|
+
downloadExtensions?: ReadonlyArray<string>;
|
|
331
|
+
/**
|
|
332
|
+
* Override the list of query parameter names checked by `siteSearch`.
|
|
333
|
+
* Order matters — first non-empty match wins.
|
|
334
|
+
*/
|
|
335
|
+
searchParams?: ReadonlyArray<string>;
|
|
336
|
+
/**
|
|
337
|
+
* Configurable button / element click tracking. Unlike `outboundClicks`
|
|
338
|
+
* (which auto-fires on every outbound anchor), this fires ONLY for elements
|
|
339
|
+
* matching a user-defined rule — the mechanism for tracking specific buttons
|
|
340
|
+
* (e.g. "Mua", "Đặt hàng") without flooding analytics with noise.
|
|
341
|
+
*
|
|
342
|
+
* In Phase 1 the rules are supplied inline here. In a later phase the same
|
|
343
|
+
* {@link ClickRule} shape is fetched from remote config so non-technical
|
|
344
|
+
* users can manage rules from the dashboard without redeploying the site.
|
|
345
|
+
*
|
|
346
|
+
* Default: undefined (no button tracking).
|
|
347
|
+
*/
|
|
348
|
+
buttonTracking?: ButtonTrackingOptions;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Top-level button-tracking config. A flat list of rules plus optional global
|
|
352
|
+
* throttle defaults that individual rules can override.
|
|
353
|
+
*/
|
|
354
|
+
interface ButtonTrackingOptions {
|
|
355
|
+
/** Rules evaluated (ascending `priority`) against every click. */
|
|
356
|
+
rules: ClickRule[];
|
|
357
|
+
/** Throttle defaults applied to rules that don't set their own. */
|
|
358
|
+
globalThrottle?: ThrottleSpec;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* One button-tracking rule. A click matches the rule when EVERY field present
|
|
362
|
+
* in `match` is satisfied by the same element (logical AND). The first
|
|
363
|
+
* matching rule (lowest `priority`) wins and fires the fixed `Clicked` event;
|
|
364
|
+
* the rule's `event.name` (if set) is attached as the `event_name` property.
|
|
365
|
+
*/
|
|
366
|
+
interface ClickRule {
|
|
367
|
+
/** Stable id (ULID from backend). Stamped onto the event as `rule_id`. */
|
|
368
|
+
id: string;
|
|
369
|
+
/** Human label, e.g. "Nút Mua hàng". Optional; stamped as `rule_name`. */
|
|
370
|
+
name?: string;
|
|
371
|
+
/** Default `true`. Set `false` to keep the rule but stop it firing. */
|
|
372
|
+
enabled?: boolean;
|
|
373
|
+
/** Lower runs first. Default `0`. */
|
|
374
|
+
priority?: number;
|
|
375
|
+
/** Conditions that select which element(s) this rule applies to. */
|
|
376
|
+
match: ClickMatch;
|
|
377
|
+
/** What event to emit when the rule matches. */
|
|
378
|
+
event: ClickEventSpec;
|
|
379
|
+
/** Properties to auto-collect from the matched element. */
|
|
380
|
+
capture?: CaptureSpec;
|
|
381
|
+
/** Per-rule noise controls (override `globalThrottle`). */
|
|
382
|
+
throttle?: ThrottleSpec;
|
|
383
|
+
/** Restrict the rule to certain pages. */
|
|
384
|
+
urlScope?: UrlMatch;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Element selection criteria. All present fields are AND-ed and evaluated
|
|
388
|
+
* against the SAME element (the nearest matching ancestor of the click target).
|
|
389
|
+
*/
|
|
390
|
+
interface ClickMatch {
|
|
391
|
+
/**
|
|
392
|
+
* Element type filter. Special values: `'button'` (button / input[submit|
|
|
393
|
+
* button|reset] / [role=button]), `'link'` (anchor), `'any'`. Any other
|
|
394
|
+
* string is treated as a CSS selector. When omitted, defaults to the
|
|
395
|
+
* actionable set (button-like + link) unless another attribute/selector
|
|
396
|
+
* criterion is present, in which case it defaults to `'any'`.
|
|
397
|
+
*/
|
|
398
|
+
element?: 'button' | 'link' | 'any' | (string & {});
|
|
399
|
+
/** Match the element's visible text. */
|
|
400
|
+
text?: TextMatch;
|
|
401
|
+
/** Match `element.id` exactly. */
|
|
402
|
+
id?: string;
|
|
403
|
+
/** Element must carry ALL of these CSS classes. */
|
|
404
|
+
classes?: string[];
|
|
405
|
+
/** Match a `data-*` attribute (presence, or exact value if `value` given). */
|
|
406
|
+
dataAttr?: DataAttrMatch;
|
|
407
|
+
/** Free-form CSS selector; matched via `element.matches(selector)`. */
|
|
408
|
+
selector?: string;
|
|
409
|
+
/** Match the URL the button/link navigates to (anchor href, formaction, form action, or data-href). */
|
|
410
|
+
destinationUrl?: DestinationMatch;
|
|
411
|
+
}
|
|
412
|
+
interface TextMatch {
|
|
413
|
+
mode: 'contains' | 'exact' | 'startsWith' | 'regex';
|
|
414
|
+
value: string;
|
|
415
|
+
/** Default `true`. Ignored for `regex` except to add the `i` flag. */
|
|
416
|
+
caseInsensitive?: boolean;
|
|
417
|
+
}
|
|
418
|
+
interface DataAttrMatch {
|
|
419
|
+
/** Full attribute name, e.g. `data-track`. */
|
|
420
|
+
name: string;
|
|
421
|
+
/** If omitted, presence is enough; otherwise exact value match. */
|
|
422
|
+
value?: string;
|
|
423
|
+
}
|
|
424
|
+
interface DestinationMatch {
|
|
425
|
+
mode: 'contains' | 'exact' | 'startsWith' | 'regex' | 'domain';
|
|
426
|
+
value: string;
|
|
427
|
+
}
|
|
428
|
+
interface UrlMatch {
|
|
429
|
+
mode: 'glob' | 'regex' | 'startsWith';
|
|
430
|
+
/** Compared against `location.pathname + location.search`. */
|
|
431
|
+
value: string;
|
|
432
|
+
}
|
|
433
|
+
/** The event emitted when a rule matches — keeps captured data clean & named. */
|
|
434
|
+
interface ClickEventSpec {
|
|
435
|
+
/**
|
|
436
|
+
* Optional user-supplied label, e.g. "Add to Cart". The event itself is
|
|
437
|
+
* always emitted as `Clicked`; this name is attached as the `event_name`
|
|
438
|
+
* property when present. Omit to fire `Clicked` with no `event_name`.
|
|
439
|
+
*/
|
|
440
|
+
name?: string;
|
|
441
|
+
/** Constant properties merged into every event this rule fires. */
|
|
442
|
+
staticProps?: Record<string, string | number | boolean>;
|
|
443
|
+
}
|
|
444
|
+
/** Allowlist of properties to auto-collect from the matched element. */
|
|
445
|
+
interface CaptureSpec {
|
|
446
|
+
/** Visible text (trimmed, capped at 200 chars, PII-scrubbed). */
|
|
447
|
+
text?: boolean;
|
|
448
|
+
/** Resolved absolute destination URL. */
|
|
449
|
+
href?: boolean;
|
|
450
|
+
/** `element.id`. */
|
|
451
|
+
elementId?: boolean;
|
|
452
|
+
/** `element.classList` as an array. */
|
|
453
|
+
classes?: boolean;
|
|
454
|
+
/** Names of `data-*` attributes to copy (PII-scrubbed). */
|
|
455
|
+
dataAttrs?: string[];
|
|
456
|
+
/** Read a value from a descendant selector (e.g. a price near the button). */
|
|
457
|
+
valueFrom?: {
|
|
458
|
+
selector: string;
|
|
459
|
+
attr?: string;
|
|
460
|
+
as: string;
|
|
461
|
+
};
|
|
462
|
+
/** A short CSS path of the element (debug / attribution). */
|
|
463
|
+
selectorPath?: boolean;
|
|
464
|
+
/** Click coordinates `{ x, y }`. */
|
|
465
|
+
position?: boolean;
|
|
466
|
+
}
|
|
467
|
+
interface ThrottleSpec {
|
|
468
|
+
/** Ignore repeat clicks on the same element within this window. Default 500. */
|
|
469
|
+
dedupeWindowMs?: number;
|
|
470
|
+
/** Cap events fired per rule per pageview. Default unlimited. */
|
|
471
|
+
maxPerPageview?: number;
|
|
472
|
+
/** Probability 0..1 an eligible click is recorded. Default 1. */
|
|
473
|
+
samplingRate?: number;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Envelope trả về từ `GET /v1/config` (remote config). SDK parse trực tiếp; chỉ
|
|
477
|
+
* dùng phần `buttonTracking`, các field nội bộ khác (nếu có) được bỏ qua.
|
|
478
|
+
*/
|
|
479
|
+
interface RemoteConfigEnvelope {
|
|
480
|
+
/** Tăng mỗi lần publish; cũng là giá trị `ETag` để cache-bust. */
|
|
481
|
+
version: number;
|
|
482
|
+
/** Phiên bản schema rule, ví dụ `"1.0"`. */
|
|
483
|
+
schemaVersion?: string;
|
|
484
|
+
/** ISO timestamp lần publish gần nhất. */
|
|
485
|
+
updatedAt?: string;
|
|
486
|
+
/** Cấu hình button-tracking đã publish. */
|
|
487
|
+
buttonTracking?: {
|
|
488
|
+
/** `false` ⇒ tắt button-tracking (SDK gỡ listener). */
|
|
489
|
+
enabled?: boolean;
|
|
490
|
+
/** Danh sách rule (giống schema inline {@link ClickRule}). */
|
|
491
|
+
rules?: ClickRule[];
|
|
492
|
+
/** Throttle mặc định cho rule không tự đặt. */
|
|
493
|
+
globalThrottle?: ThrottleSpec;
|
|
494
|
+
};
|
|
495
|
+
/** Kill-switch QR-login Zalo từ dashboard. `enabled: false` ⇒ `loginWithZalo()` reject ngay. */
|
|
496
|
+
zaloQrLogin?: {
|
|
497
|
+
enabled?: boolean;
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Lý do `loginWithZalo()` thất bại (`ZaloQrLoginError.reason`). LƯU Ý phạm vi
|
|
502
|
+
* reject khác nhau:
|
|
503
|
+
* - `'not-initialized' | 'disabled' | 'consent-required' | 'invalid-project-key'
|
|
504
|
+
* | 'project-suspended' | 'network'`: reject ngay lời gọi `loginWithZalo(...)`
|
|
505
|
+
* (trước khi có {@link ZaloQrLoginSession}) — lỗi thiết lập phiên, không có
|
|
506
|
+
* `session`/`session.result` nào để bắt.
|
|
507
|
+
* - `'expired' | 'denied' | 'cancelled'`: reject {@link ZaloQrLoginSession.result}
|
|
508
|
+
* — xảy ra SAU khi đã có `session` (QR đã hiển thị), khi chờ kết quả quét.
|
|
509
|
+
*
|
|
510
|
+
* Bọc `try/catch` quanh CẢ hai điểm `await` (xem ví dụ ở `loginWithZalo` JSDoc)
|
|
511
|
+
* để không bỏ sót nhóm đầu.
|
|
512
|
+
*
|
|
513
|
+
* `'invalid-project-key'`/`'project-suspended'` tách riêng khỏi `'network'` dựa
|
|
514
|
+
* theo HTTP status server trả về (401/403) — đây là lỗi CẤU HÌNH (project key
|
|
515
|
+
* gõ sai/thiếu, hoặc project bị khoá), KHÔNG PHẢI lỗi mạng/CORS thật. Trước đây
|
|
516
|
+
* cả 2 nhóm bị gộp chung `'network'`, khiến integrator debug sai hướng.
|
|
517
|
+
*/
|
|
518
|
+
type ZaloQrLoginErrorReason = 'not-initialized' | 'disabled' | 'consent-required' | 'invalid-project-key' | 'project-suspended' | 'network' | 'expired' | 'denied' | 'cancelled';
|
|
519
|
+
/** Tuỳ chọn cho `loginWithZalo()`. */
|
|
520
|
+
interface ZaloQrLoginOptions {
|
|
521
|
+
/** Ưu tiên SSE (mặc định `true`); `false` ⇒ chỉ dùng poll ngay từ đầu. */
|
|
522
|
+
preferSse?: boolean;
|
|
523
|
+
/** Khoảng poll fallback (ms). Mặc định 2000. */
|
|
524
|
+
pollIntervalMs?: number;
|
|
525
|
+
/**
|
|
526
|
+
* Mặc định `true` — quét thành công thì SDK **tự chọn** giữa `identify()`/`alias()`, integrator
|
|
527
|
+
* không cần tự nghĩ về ca "site đã có tài khoản riêng":
|
|
528
|
+
* - Chưa từng `identify()` trước đó → Zalo QR là cách đăng nhập chính → `identify(String(contactId), {...})`.
|
|
529
|
+
* - Đã có `userId` từ trước (site tự `identify()` sau login thường của họ) → **không ghi đè** —
|
|
530
|
+
* `alias(String(contactId), userId cũ)` để backend biết 2 ID là cùng một người, `userId`
|
|
531
|
+
* hiện tại giữ nguyên, lịch sử tracking không bị "nhảy" giữa chừng.
|
|
532
|
+
*
|
|
533
|
+
* Đặt `false` để tắt hẳn cả 2 nhánh trên, tự quyết định 100% bằng `result.contactId` — xem
|
|
534
|
+
* "autoIdentify: false" trong docs.
|
|
535
|
+
*/
|
|
536
|
+
autoIdentify?: boolean;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Kết quả khi người dùng quét QR và xác thực Zalo thành công.
|
|
540
|
+
*
|
|
541
|
+
* KHÔNG có `phone` — số điện thoại thật KHÔNG được trả về client (cùng lý do đã bỏ
|
|
542
|
+
* `apiToken`/`loginToken`: PII/credential lộ ra bất kỳ script nào chạy chung trang hoặc
|
|
543
|
+
* ai mở DevTools). `contactId` là định danh dùng để `identify()` — nếu cần số điện thoại
|
|
544
|
+
* cho nghiệp vụ, tự tra cứu ở backend của bạn bằng `contactId` (đã xác thực thật).
|
|
545
|
+
*/
|
|
546
|
+
interface ZaloQrLoginResult {
|
|
547
|
+
contactId?: number;
|
|
548
|
+
/** Họ tên đầy đủ của contact trong CDP (nếu có). */
|
|
549
|
+
fullName?: string;
|
|
550
|
+
/** Tên hạng thành viên hiện tại (nếu có). */
|
|
551
|
+
level?: string;
|
|
552
|
+
/** Điểm kinh nghiệm/tích luỹ hiện tại (nếu có). */
|
|
553
|
+
xp?: number;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Phiên QR-login đang chờ người dùng quét. SDK KHÔNG tự vẽ QR — host page tự
|
|
557
|
+
* render từ `qrPayload` (deep-link Mini App Zalo), rồi `await session.result`
|
|
558
|
+
* để nhận kết quả khi quét xong (hoặc bắt lỗi nếu hết hạn/bị từ chối/huỷ).
|
|
559
|
+
*/
|
|
560
|
+
interface ZaloQrLoginSession {
|
|
561
|
+
sessionToken: string;
|
|
562
|
+
/** Deep-link Mini App Zalo — host tự vẽ thành mã QR. */
|
|
563
|
+
qrPayload: string;
|
|
564
|
+
expiresInSeconds: number;
|
|
565
|
+
/**
|
|
566
|
+
* Resolve khi quét xong & backend xác nhận; reject với {@link ZaloQrLoginError}
|
|
567
|
+
* có `reason` là `'expired' | 'denied' | 'cancelled'` (xem {@link ZaloQrLoginErrorReason}
|
|
568
|
+
* để phân biệt với lỗi thiết lập phiên, vốn reject lời gọi `loginWithZalo()` chứ
|
|
569
|
+
* không phải field này).
|
|
570
|
+
*/
|
|
571
|
+
result: Promise<ZaloQrLoginResult>;
|
|
572
|
+
/** Huỷ sớm phía client (đóng SSE/dừng poll, reject `result` với `'cancelled'`). Không huỷ được phiên phía server — phiên tự hết hạn theo TTL. */
|
|
573
|
+
cancel: () => void;
|
|
574
|
+
/**
|
|
575
|
+
* Tạo phiên MỚI (QR mới) — dùng khi `result` đã reject với `'expired'` (hoặc `'denied'`,
|
|
576
|
+
* hoặc để chủ động "làm mới QR" theo yêu cầu người dùng). Tự huỷ phiên hiện tại trước khi
|
|
577
|
+
* tạo phiên mới (an toàn gọi ngay cả khi phiên đã kết thúc — no-op phần huỷ đó); trả về
|
|
578
|
+
* {@link ZaloQrLoginSession} MỚI hoàn toàn (không mutate session cũ) — host render lại QR
|
|
579
|
+
* từ `qrPayload` của session mới rồi `await` `result` mới như bình thường.
|
|
580
|
+
*/
|
|
581
|
+
retry: () => Promise<ZaloQrLoginSession>;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Each entry in `window.cnvQ` is a tuple `[methodName, ...args]`.
|
|
585
|
+
* The async snippet seeds the queue as a plain Array; once the SDK loads,
|
|
586
|
+
* it drains the queue and replaces `.push` with a synchronous dispatcher.
|
|
587
|
+
*
|
|
588
|
+
* Discriminated by tuple index 0 for exhaustive type-narrowing.
|
|
589
|
+
*/
|
|
590
|
+
type QueuedCommand = readonly ['init', string, SDKOptions?] | readonly ['pageview', string?, Record<string, JSONValue>?] | readonly ['page', string?, Record<string, JSONValue>?] | readonly ['screen', string?, Record<string, JSONValue>?] | readonly ['track', string, Record<string, JSONValue>?] | readonly ['identify', string, UserTraits?] | readonly ['alias', string, string?] | readonly ['group', string, Record<string, JSONValue>?] | readonly ['reset'] | readonly ['flush'] | readonly ['consent', Partial<ContextConsent>] | readonly ['set', Partial<SDKOptions>];
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* ============================================================================
|
|
594
|
+
* queue — Queue Processor (Sub-task 3.5)
|
|
595
|
+
* ============================================================================
|
|
596
|
+
*
|
|
597
|
+
* The async snippet seeds `window.cnvQ = []` and pushes `[methodName, ...args]`
|
|
598
|
+
* tuples while the SDK is still loading. Once this file runs, we:
|
|
599
|
+
*
|
|
600
|
+
* 1. Snapshot whatever the snippet pushed (the pending FIFO backlog).
|
|
601
|
+
* 2. Replace `cnvQ.push` with a proxy that *synchronously* dispatches.
|
|
602
|
+
* 3. Drain the snapshot through the dispatcher in original order.
|
|
603
|
+
* 4. Mark `cnvQ.__loaded = true` so duplicate snippet includes no-op.
|
|
604
|
+
*
|
|
605
|
+
* Ordering note: step 2 happens BEFORE step 3 on purpose. If a dispatched
|
|
606
|
+
* command synchronously triggers another `cnvQ.push` (e.g., a user's init
|
|
607
|
+
* options has an `onReady` callback that fires a `track`), the new push
|
|
608
|
+
* also goes through the proxy and stays in FIFO order with the backlog
|
|
609
|
+
* rather than racing the drain loop.
|
|
610
|
+
*
|
|
611
|
+
* Every dispatch is wrapped in try/catch (Sub-task 5.2 — silent-fail
|
|
612
|
+
* contract). A malformed command MUST NOT crash the host page or stop
|
|
613
|
+
* the rest of the queue from draining.
|
|
614
|
+
* ============================================================================
|
|
615
|
+
*/
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Surface the queue forwards inbound tuples to. Each method matches one
|
|
619
|
+
* `QueuedCommand` discriminator. The runtime (in `index.ts`) implements this.
|
|
620
|
+
*/
|
|
621
|
+
interface Dispatcher {
|
|
622
|
+
init(projectId: string, options?: SDKOptions): void;
|
|
623
|
+
pageview(name?: string, properties?: Record<string, JSONValue>): void;
|
|
624
|
+
page(name?: string, properties?: Record<string, JSONValue>): void;
|
|
625
|
+
screen(name?: string, properties?: Record<string, JSONValue>): void;
|
|
626
|
+
track(event: string, properties?: Record<string, JSONValue>): void;
|
|
627
|
+
identify(userId: string, traits?: UserTraits): void;
|
|
628
|
+
alias(userId: string, previousId?: string): void;
|
|
629
|
+
group(groupId: string, traits?: Record<string, JSONValue>): void;
|
|
630
|
+
reset(): void;
|
|
631
|
+
flush(): void;
|
|
632
|
+
consent(state: Partial<ContextConsent>): void;
|
|
633
|
+
set(options: Partial<SDKOptions>): void;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* ============================================================================
|
|
638
|
+
* runtime — SDK state holder + public API (Sub-tasks 3.1 / 3.2 / 3.3 / 3.4)
|
|
639
|
+
* ============================================================================
|
|
640
|
+
*
|
|
641
|
+
* Implements the {@link Dispatcher} surface consumed by the Queue Processor.
|
|
642
|
+
* Module-scoped state (project id, resolved options, transport instance,
|
|
643
|
+
* consent) lives here as a singleton. `createRuntime()` exists so tests can
|
|
644
|
+
* spin up an isolated copy without leaking state between cases.
|
|
645
|
+
*
|
|
646
|
+
* Public API and what each method does:
|
|
647
|
+
* init(projectId, options?) → set up runtime, build transport
|
|
648
|
+
* track(event, properties?) → emit `type:"track"`
|
|
649
|
+
* identify(userId, traits?) → emit `type:"identify"`, persist uid
|
|
650
|
+
* reset() → rotate anonymousId, drop userId
|
|
651
|
+
* flush() → force-flush the transport buffer
|
|
652
|
+
* consent(state) → merge consent flags
|
|
653
|
+
* set(options) → patch SDK options at runtime
|
|
654
|
+
*
|
|
655
|
+
* RETIRED (platform-wide, mọi site — ke_hoach_gioi_han_event_cho_phep.md):
|
|
656
|
+
* `pageview()`/`page()`/`screen()`/`group()`/`alias()` vẫn còn tồn tại như no-op (giữ chữ ký hàm
|
|
657
|
+
* để không phá interface `Dispatcher`/`Runtime` và code cũ gọi vào không throw), nhưng KHÔNG còn
|
|
658
|
+
* gửi gì đi nữa cho BẤT KỲ site nào — backend (`CnvIngestService.enforceEventAllowed`) đã luôn từ
|
|
659
|
+
* chối các type này khi project resolve được rồi, nên quyết định là bỏ hẳn khả năng gửi ở SDK
|
|
660
|
+
* luôn, không riêng site nào. Chỉ còn `track()` + `identify()` hoạt động thật — whitelist tên
|
|
661
|
+
* event nào được phép là việc của backend (`allowedEventNames`), SDK không tự gate gì thêm ở phía
|
|
662
|
+
* client nữa (tránh khai báo trùng 2 nơi dễ lệch nhau).
|
|
663
|
+
*
|
|
664
|
+
* Init order matters:
|
|
665
|
+
* 1. Cookie / LS reads happen on demand (lazy) — every event resolves
|
|
666
|
+
* its own anonymousId + sessionId at message-build time so a re-init
|
|
667
|
+
* with new cookieDomain takes effect on the very next event.
|
|
668
|
+
* 2. Transport is recreated whenever endpoint / batch settings change.
|
|
669
|
+
*
|
|
670
|
+
* Silent-fail (Sub-task 5.2):
|
|
671
|
+
* Every public method is wrapped in try/catch. A bug inside the SDK
|
|
672
|
+
* MUST NEVER throw out into the host page.
|
|
673
|
+
* ============================================================================
|
|
674
|
+
*/
|
|
675
|
+
|
|
676
|
+
/** Public-facing handle that exposes the SDK methods + a few internals. */
|
|
677
|
+
interface Runtime extends Dispatcher {
|
|
678
|
+
readonly initialized: boolean;
|
|
679
|
+
readonly projectId: string | null;
|
|
680
|
+
/** Gateway base URL đang dùng — cho các module bên ngoài Dispatcher (vd `zaloQrLogin`). */
|
|
681
|
+
readonly endpoint: string;
|
|
682
|
+
/** Đọc nhanh (đồng bộ, từ cache remote-config) cờ bật/tắt QR-login Zalo cho project hiện tại. */
|
|
683
|
+
isZaloQrLoginEnabled(): boolean;
|
|
684
|
+
/** `true` khi cả `analytics` và `marketing` đều được đồng ý — ngưỡng `loginWithZalo()` yêu cầu vì thu thập SĐT. */
|
|
685
|
+
readonly consentGranted: boolean;
|
|
686
|
+
/** Tear down timers + listeners. Test-only in production. */
|
|
687
|
+
destroy(): void;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* ============================================================================
|
|
692
|
+
* zaloQrLogin — đăng nhập bằng Zalo qua QR cho website nhúng SDK
|
|
693
|
+
* ============================================================================
|
|
694
|
+
*
|
|
695
|
+
* `loginWithZalo()` là API ĐẦU TIÊN của SDK không theo hợp đồng "never throw,
|
|
696
|
+
* fire-and-forget" của các method khác (track/identify/...) — nó trả Promise
|
|
697
|
+
* thật, và có THỂ REJECT Ở 2 ĐIỂM khác nhau (xem `ZaloQrLoginErrorReason` ở
|
|
698
|
+
* types.ts để biết chi tiết từng reason):
|
|
699
|
+
* 1. Lời gọi `loginWithZalo()` chính nó — lỗi thiết lập phiên (chưa init,
|
|
700
|
+
* dashboard tắt, thiếu consent, lỗi mạng khi tạo phiên).
|
|
701
|
+
* 2. `session.result` — lỗi xảy ra SAU khi đã có phiên/đã hiển thị QR
|
|
702
|
+
* (hết hạn, bị từ chối, bị huỷ).
|
|
703
|
+
* Consumer cần bọc try/catch quanh CẢ HAI điểm await. Vì cần tương tác 2
|
|
704
|
+
* chiều thực sự (đăng ký phiên → chờ QR được quét), hàm này KHÔNG đi qua
|
|
705
|
+
* queue protocol `window.cnvQ` — chỉ dùng được qua ES-module import.
|
|
706
|
+
*
|
|
707
|
+
* SDK không tự vẽ QR: trả `qrPayload` (deep-link Mini App) để host page tự
|
|
708
|
+
* render `<img>`/canvas, đúng vai trò "tracker" chứ không phải "auth widget"
|
|
709
|
+
* (xem cách `demo/index.html` tự vẽ modal login cho `identify()`).
|
|
710
|
+
*
|
|
711
|
+
* Kênh nhận kết quả: SSE (`GET {endpoint}/zalo-qr/stream`) làm chính, tự
|
|
712
|
+
* fallback sang poll (`GET {endpoint}/zalo-qr/status`) nếu `EventSource`
|
|
713
|
+
* không tồn tại hoặc báo lỗi kéo dài — cùng triết lý "luôn có phương án dự
|
|
714
|
+
* phòng" mà `transport.ts` áp dụng (sendBeacon → fetch).
|
|
715
|
+
*
|
|
716
|
+
* Làm mới QR hết hạn: `session.retry()` — tự huỷ phiên hiện tại (an toàn dù đã
|
|
717
|
+
* hết hạn/bị từ chối/thành công từ trước) rồi tạo phiên MỚI với cùng host/options
|
|
718
|
+
* ban đầu, trả về `ZaloQrLoginSession` mới để host render lại QR + await `result`
|
|
719
|
+
* mới. Không cần tự lưu lại `host`/`opts` hay gọi lại `loginWithZalo()` thủ công.
|
|
720
|
+
* ============================================================================
|
|
721
|
+
*/
|
|
722
|
+
|
|
723
|
+
/** Lỗi nghiệp vụ của `loginWithZalo()` — xem {@link ZaloQrLoginErrorReason} cho phạm vi reject của từng reason. */
|
|
724
|
+
declare class ZaloQrLoginError extends Error {
|
|
725
|
+
readonly reason: ZaloQrLoginErrorReason;
|
|
726
|
+
constructor(reason: ZaloQrLoginErrorReason);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* ============================================================================
|
|
731
|
+
* cnv-track.js — public entry point
|
|
732
|
+
* ============================================================================
|
|
733
|
+
*
|
|
734
|
+
* Wires the runtime (`./core/runtime.ts`) into the Queue Processor
|
|
735
|
+
* (`./core/queue.ts`) and exposes the public API surface.
|
|
736
|
+
*
|
|
737
|
+
* Two consumer paths:
|
|
738
|
+
*
|
|
739
|
+
* 1. Script-tag (IIFE) via snippet:
|
|
740
|
+
* Snippet seeds `window.cnvQ`, this module auto-installs the Queue
|
|
741
|
+
* Processor on import, drains the backlog, and starts dispatching.
|
|
742
|
+
*
|
|
743
|
+
* 2. ES module:
|
|
744
|
+
* `import cnvTrack from '@cnv/track'`
|
|
745
|
+
* `cnvTrack.init('PRJ_X')`
|
|
746
|
+
* Side-effectful auto-install still runs (a no-op if `window.cnvQ`
|
|
747
|
+
* is empty); consumers can drive the SDK directly via the default
|
|
748
|
+
* export or by pushing tuples through `window.cnvQ`.
|
|
749
|
+
* ============================================================================
|
|
750
|
+
*/
|
|
751
|
+
|
|
752
|
+
/** Library version. Replaced at build time by @rollup/plugin-replace. */
|
|
753
|
+
declare const VERSION: string;
|
|
754
|
+
/** Library name. Replaced at build time by @rollup/plugin-replace. */
|
|
755
|
+
declare const NAME: string;
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Đăng nhập bằng Zalo qua QR — API duy nhất của SDK trả Promise thật thay vì
|
|
759
|
+
* silent-fail (xem `core/zaloQrLogin.ts`). KHÔNG đi qua queue protocol
|
|
760
|
+
* `window.cnvQ` — chỉ dùng được qua ES-module import. Có THỂ REJECT Ở 2 ĐIỂM
|
|
761
|
+
* khác nhau (xem {@link ZaloQrLoginErrorReason}) — bọc try/catch quanh cả hai
|
|
762
|
+
* `await`, và gọi `session.cancel()` khi host đóng UI QR sớm (đóng SSE/dừng poll):
|
|
763
|
+
*
|
|
764
|
+
* ```ts
|
|
765
|
+
* import { loginWithZalo, ZaloQrLoginError } from '@cnv-vn/track';
|
|
766
|
+
*
|
|
767
|
+
* let session;
|
|
768
|
+
* try {
|
|
769
|
+
* session = await loginWithZalo(); // có thể reject: not-initialized/disabled/consent-required/network
|
|
770
|
+
* renderQr(session.qrPayload);
|
|
771
|
+
* } catch (err) {
|
|
772
|
+
* if (err instanceof ZaloQrLoginError) showError(err.reason);
|
|
773
|
+
* return;
|
|
774
|
+
* }
|
|
775
|
+
*
|
|
776
|
+
* // modal đóng sớm (user bấm huỷ): session.cancel();
|
|
777
|
+
*
|
|
778
|
+
* try {
|
|
779
|
+
* const { contactId, fullName } = await session.result; // có thể reject: expired/denied/cancelled
|
|
780
|
+
* onLoggedIn(contactId, fullName);
|
|
781
|
+
* } catch (err) {
|
|
782
|
+
* if (err instanceof ZaloQrLoginError) showError(err.reason);
|
|
783
|
+
* }
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
declare function loginWithZalo(opts?: ZaloQrLoginOptions): Promise<ZaloQrLoginSession>;
|
|
787
|
+
/**
|
|
788
|
+
* Public SDK handle. Methods correspond 1:1 with `QueuedCommand` verbs so
|
|
789
|
+
* `window.cnvQ.push(['track', 'X'])` and `cnvTrack.track('X')` are interchangeable.
|
|
790
|
+
* `loginWithZalo` is attached for ergonomics but is NOT part of `QueuedCommand`.
|
|
791
|
+
*/
|
|
792
|
+
declare const cnvTrack: Runtime & {
|
|
793
|
+
version: string;
|
|
794
|
+
name: string;
|
|
795
|
+
schemaVersion: "1.0";
|
|
796
|
+
loginWithZalo: typeof loginWithZalo;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
export { NAME, SCHEMA_VERSION, VERSION, ZaloQrLoginError, cnvTrack as default, loginWithZalo };
|
|
800
|
+
export type { AutotrackOptions, ButtonTrackingOptions, CaptureSpec, ClickEventSpec, ClickMatch, ClickRule, ContextCampaign, ContextConsent, ContextLibrary, ContextPage, ContextScreen, DataAttrMatch, DestinationMatch, JSONValue, MessageType, PayloadContext, QueuedCommand, RemoteConfigEnvelope, RemoteConfigOptions, SDKOptions, SegmentBatchRequest, SegmentMessage, TextMatch, ThrottleSpec, UrlMatch, UserTraits, ZaloQrLoginErrorReason, ZaloQrLoginOptions, ZaloQrLoginResult, ZaloQrLoginSession };
|