@mosovn/echo 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/native.d.ts CHANGED
@@ -36,6 +36,27 @@ interface EchoConfig {
36
36
  trackSessions?: boolean;
37
37
  /** [Native only] Track JS errors via ErrorUtils + unhandledrejection. Default true. */
38
38
  trackErrors?: boolean;
39
+ /** [Web only] Session replay configuration. Default OFF (replay is opt-in). */
40
+ replay?: ReplayConfig | boolean;
41
+ }
42
+ interface ReplayConfig {
43
+ /** Master toggle. If config is passed without this set, defaults to true. */
44
+ enabled?: boolean;
45
+ /**
46
+ * Fraction of sessions to record [0..1]. Decision is sticky per sessionId,
47
+ * so a recorded user stays recorded for the whole session. Default 0.1.
48
+ */
49
+ sampleRate?: number;
50
+ /** CSS selectors whose text is masked as ▓▓▓ in replay. */
51
+ maskTextSelectors?: string[];
52
+ /** CSS selectors whose subtree is fully removed from replay. */
53
+ blockSelectors?: string[];
54
+ /** URL path regexes/prefixes to NEVER record (vd /reset-password). */
55
+ blockUrlPatterns?: (string | RegExp)[];
56
+ /** Chunk flush interval in ms. Default 30000. */
57
+ chunkIntervalMs?: number;
58
+ /** Endpoint path. Default '/v1/replays/chunk'. */
59
+ chunkPath?: string;
39
60
  }
40
61
  interface AutoCaptureConfig {
41
62
  pageviews?: boolean;
package/dist/native.js CHANGED
@@ -1,2 +1,2 @@
1
- import {Platform,AppState}from'react-native';import E from'@react-native-async-storage/async-storage';var f=new Map;async function w(s){try{let t=await E.multiGet(s);for(let[e,i]of t)i!==null&&f.set(e,i);}catch{}}function u(s){return f.get(s)??null}function r(s,t){f.set(s,t),E.setItem(s,t).catch(()=>{});}function l(s){f.delete(s),E.removeItem(s).catch(()=>{});}function a(){let s=BigInt(Date.now()),t=new Uint8Array(10);crypto.getRandomValues(t);let e=new Uint8Array(16);e[0]=Number(s>>40n&0xffn),e[1]=Number(s>>32n&0xffn),e[2]=Number(s>>24n&0xffn),e[3]=Number(s>>16n&0xffn),e[4]=Number(s>>8n&0xffn),e[5]=Number(s&0xffn),e[6]=t[0]&15|112,e[7]=t[1],e[8]=t[2]&63|128,e[9]=t[3],e[10]=t[4],e[11]=t[5],e[12]=t[6],e[13]=t[7],e[14]=t[8],e[15]=t[9];let i=Array.from(e,n=>n.toString(16).padStart(2,"0")).join("");return `${i.slice(0,8)}-${i.slice(8,12)}-${i.slice(12,16)}-${i.slice(16,20)}-${i.slice(20)}`}var g="echo_anonymous_id",p="echo_user_id",h="echo_session_id",d="echo_last_activity",b=[g,p,h,d],m=class{constructor(t){this.sessionTimeoutMs=t;let e=u(g);e||(e=a(),r(g,e)),this.anonymousId=e,this.userId=u(p);let i=Date.now(),n=u(h),o=parseInt(u(d)||"0",10);n&&i-o<t?this.sessionId=n:(this.sessionId=a(),r(h,this.sessionId)),this.lastActivity=i,r(d,String(i));}touch(){let t=Date.now(),e=t-this.lastActivity>=this.sessionTimeoutMs,i;return e&&(i=this.sessionId,this.sessionId=a(),r(h,this.sessionId)),this.lastActivity=t,r(d,String(t)),{sessionStarted:e,oldSessionId:i}}setUserId(t){this.userId=t,r(p,t);}reset(){this.userId=null,l(p),this.anonymousId=a(),r(g,this.anonymousId),this.sessionId=a(),r(h,this.sessionId),this.lastActivity=Date.now(),r(d,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var I="echo_queue_v1",_=1e3,y=class{constructor(){this.buffer=[];}restore(){try{let t=u(I);t&&(this.buffer=JSON.parse(t));}catch{this.buffer=[];}}persist(){try{r(I,JSON.stringify(this.buffer));}catch{}}enqueue(t){this.buffer.length>=_&&this.buffer.shift(),this.buffer.push(t),this.persist();}size(){return this.buffer.length}drain(){let t=this.buffer;return this.buffer=[],l(I),t}requeue(t){this.buffer=t.concat(this.buffer).slice(0,_),this.persist();}};async function A(s,t){if(s.length===0)return true;let e=t.endpoint.replace(/\/+$/,"")+"/v1/events";try{return (await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":t.writeKey},body:JSON.stringify({events:s})})).ok}catch{return false}}async function k(s,t){let e=t.endpoint.replace(/\/+$/,"")+"/v1/identify";try{return (await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":t.writeKey},body:JSON.stringify(s)})).ok}catch{return false}}var U="https://events.moso.vn",q=50,x=1e4,K=1800*1e3,C=["echo_queue_v1"],v=class{constructor(t){this.flushTimer=null;this.flushing=false;this.ready=false;if(!t.writeKey)throw new Error("@mosovn/echo-rn: writeKey is required");this.config={writeKey:t.writeKey,endpoint:t.endpoint??U,flushAt:t.flushAt??q,flushInterval:t.flushInterval??x,sessionTimeoutMs:t.sessionTimeoutMs??K,debug:t.debug??false,trackSessions:t.trackSessions??true,trackErrors:t.trackErrors??true,...t},this.init();}debugLog(...t){this.config.debug&&console.log("[echo-rn]",...t);}async init(){await w([...b,...C]),this.identity=new m(this.config.sessionTimeoutMs),this.queue=new y,this.queue.restore(),this.ready=true,this.debugLog("init complete",{anon:this.identity.getAnonymousId(),session:this.identity.getSessionId()}),this.startFlushTimer(),this.config.trackSessions&&this.installAppStateTracking(),this.config.trackErrors&&this.installErrorTracking();}track(t,e){this.enqueueEvent(t,"custom",e);}identify(t,e){if(!this.ready){this.waitReady().then(()=>this.identify(t,e));return}this.identity.setUserId(t),this.enqueueEvent("$identify","auto",e),k({userId:t,anonymousId:this.identity.getAnonymousId(),traits:e||{}},{writeKey:this.config.writeKey,endpoint:this.config.endpoint});}setUserProperties(t){if(!this.ready){this.waitReady().then(()=>this.setUserProperties(t));return}let e=this.identity.getUserId();e?this.identify(e,t):this.enqueueEvent("$set_user_properties","auto",t);}screen(t,e){this.enqueueEvent("$screen","auto",{name:t,...e});}async reset(){await this.waitReady(),this.identity.reset(),this.queue.drain(),this.debugLog("reset done");}async flush(){if(await this.waitReady(),this.flushing||this.queue.size()===0)return;this.flushing=true;let t=this.queue.drain();await A(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",t.length):(this.queue.requeue(t),this.debugLog("flush failed, requeued",t.length)),this.flushing=false;}getAnonymousId(){return this.ready?this.identity.getAnonymousId():""}getUserId(){return this.ready?this.identity.getUserId():null}getSessionId(){return this.ready?this.identity.getSessionId():""}waitReady(){return this.ready?Promise.resolve():new Promise(t=>{let e=()=>this.ready?t():setTimeout(e,50);e();})}enqueueEvent(t,e,i){if(!this.ready){this.waitReady().then(()=>this.enqueueEvent(t,e,i));return}let{sessionStarted:n,oldSessionId:o}=this.identity.touch();n&&o&&(this.pushRaw("$session_end","auto",void 0,o),this.pushRaw("$session_start","auto")),this.pushRaw(t,e,i);}pushRaw(t,e,i,n){let o={_id:a(),eventName:t,eventType:e,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:n??this.identity.getSessionId(),properties:i,context:this.collectContext()},S=this.config.beforeSend,c=S?S(o):o;c&&(this.queue.enqueue(c),this.debugLog("enqueue",c.eventName,c._id),this.queue.size()>=this.config.flushAt&&this.flush());}collectContext(){let t={lib:{name:"@mosovn/echo",version:"0.2.0"},os:{name:Platform.OS,version:String(Platform.Version)}};return this.config.app&&(t.app=this.config.app),t}startFlushTimer(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}installAppStateTracking(){let t=AppState.currentState;this.appStateSub=AppState.addEventListener("change",e=>{e!==t&&(this.debugLog("appstate",t,"\u2192",e),(e==="background"||e==="inactive")&&this.flush(),t=e);});}installErrorTracking(){let t=globalThis.ErrorUtils;if(t){let i=t.getGlobalHandler();t.setGlobalHandler((n,o)=>{this.track("$error",{message:n?.message,stack:n?.stack,fatal:!!o}),i?.(n,o);});}let e=i=>{let n=i?.reason;this.track("$error",{kind:"unhandledrejection",message:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0});};globalThis.addEventListener?.("unhandledrejection",e);}};function X(s){return new v(s)}
1
+ import {Platform,AppState}from'react-native';import E from'@react-native-async-storage/async-storage';var f=new Map;async function w(s){try{let t=await E.multiGet(s);for(let[e,i]of t)i!==null&&f.set(e,i);}catch{}}function u(s){return f.get(s)??null}function r(s,t){f.set(s,t),E.setItem(s,t).catch(()=>{});}function l(s){f.delete(s),E.removeItem(s).catch(()=>{});}function a(){let s=BigInt(Date.now()),t=new Uint8Array(10);crypto.getRandomValues(t);let e=new Uint8Array(16);e[0]=Number(s>>40n&0xffn),e[1]=Number(s>>32n&0xffn),e[2]=Number(s>>24n&0xffn),e[3]=Number(s>>16n&0xffn),e[4]=Number(s>>8n&0xffn),e[5]=Number(s&0xffn),e[6]=t[0]&15|112,e[7]=t[1],e[8]=t[2]&63|128,e[9]=t[3],e[10]=t[4],e[11]=t[5],e[12]=t[6],e[13]=t[7],e[14]=t[8],e[15]=t[9];let i=Array.from(e,n=>n.toString(16).padStart(2,"0")).join("");return `${i.slice(0,8)}-${i.slice(8,12)}-${i.slice(12,16)}-${i.slice(16,20)}-${i.slice(20)}`}var g="echo_anonymous_id",p="echo_user_id",h="echo_session_id",d="echo_last_activity",b=[g,p,h,d],m=class{constructor(t){this.sessionTimeoutMs=t;let e=u(g);e||(e=a(),r(g,e)),this.anonymousId=e,this.userId=u(p);let i=Date.now(),n=u(h),o=parseInt(u(d)||"0",10);n&&i-o<t?this.sessionId=n:(this.sessionId=a(),r(h,this.sessionId)),this.lastActivity=i,r(d,String(i));}touch(){let t=Date.now(),e=t-this.lastActivity>=this.sessionTimeoutMs,i;return e&&(i=this.sessionId,this.sessionId=a(),r(h,this.sessionId)),this.lastActivity=t,r(d,String(t)),{sessionStarted:e,oldSessionId:i}}setUserId(t){this.userId=t,r(p,t);}reset(){this.userId=null,l(p),this.anonymousId=a(),r(g,this.anonymousId),this.sessionId=a(),r(h,this.sessionId),this.lastActivity=Date.now(),r(d,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var I="echo_queue_v1",_=1e3,y=class{constructor(){this.buffer=[];}restore(){try{let t=u(I);t&&(this.buffer=JSON.parse(t));}catch{this.buffer=[];}}persist(){try{r(I,JSON.stringify(this.buffer));}catch{}}enqueue(t){this.buffer.length>=_&&this.buffer.shift(),this.buffer.push(t),this.persist();}size(){return this.buffer.length}drain(){let t=this.buffer;return this.buffer=[],l(I),t}requeue(t){this.buffer=t.concat(this.buffer).slice(0,_),this.persist();}};async function A(s,t){if(s.length===0)return true;let e=t.endpoint.replace(/\/+$/,"")+"/v1/events";try{return (await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":t.writeKey},body:JSON.stringify({events:s})})).ok}catch{return false}}async function k(s,t){let e=t.endpoint.replace(/\/+$/,"")+"/v1/identify";try{return (await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":t.writeKey},body:JSON.stringify(s)})).ok}catch{return false}}var U="https://events.moso.vn",q=50,x=1e4,K=1800*1e3,C=["echo_queue_v1"],v=class{constructor(t){this.flushTimer=null;this.flushing=false;this.ready=false;if(!t.writeKey)throw new Error("@mosovn/echo-rn: writeKey is required");this.config={writeKey:t.writeKey,endpoint:t.endpoint??U,flushAt:t.flushAt??q,flushInterval:t.flushInterval??x,sessionTimeoutMs:t.sessionTimeoutMs??K,debug:t.debug??false,trackSessions:t.trackSessions??true,trackErrors:t.trackErrors??true,...t},this.init();}debugLog(...t){this.config.debug&&console.log("[echo-rn]",...t);}async init(){await w([...b,...C]),this.identity=new m(this.config.sessionTimeoutMs),this.queue=new y,this.queue.restore(),this.ready=true,this.debugLog("init complete",{anon:this.identity.getAnonymousId(),session:this.identity.getSessionId()}),this.startFlushTimer(),this.config.trackSessions&&this.installAppStateTracking(),this.config.trackErrors&&this.installErrorTracking();}track(t,e){this.enqueueEvent(t,"custom",e);}identify(t,e){if(!this.ready){this.waitReady().then(()=>this.identify(t,e));return}this.identity.setUserId(t),this.enqueueEvent("$identify","auto",e),k({userId:t,anonymousId:this.identity.getAnonymousId(),traits:e||{}},{writeKey:this.config.writeKey,endpoint:this.config.endpoint});}setUserProperties(t){if(!this.ready){this.waitReady().then(()=>this.setUserProperties(t));return}let e=this.identity.getUserId();e?this.identify(e,t):this.enqueueEvent("$set_user_properties","auto",t);}screen(t,e){this.enqueueEvent("$screen","auto",{name:t,...e});}async reset(){await this.waitReady(),this.identity.reset(),this.queue.drain(),this.debugLog("reset done");}async flush(){if(await this.waitReady(),this.flushing||this.queue.size()===0)return;this.flushing=true;let t=this.queue.drain();await A(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",t.length):(this.queue.requeue(t),this.debugLog("flush failed, requeued",t.length)),this.flushing=false;}getAnonymousId(){return this.ready?this.identity.getAnonymousId():""}getUserId(){return this.ready?this.identity.getUserId():null}getSessionId(){return this.ready?this.identity.getSessionId():""}waitReady(){return this.ready?Promise.resolve():new Promise(t=>{let e=()=>this.ready?t():setTimeout(e,50);e();})}enqueueEvent(t,e,i){if(!this.ready){this.waitReady().then(()=>this.enqueueEvent(t,e,i));return}let{sessionStarted:n,oldSessionId:o}=this.identity.touch();n&&o&&(this.pushRaw("$session_end","auto",void 0,o),this.pushRaw("$session_start","auto")),this.pushRaw(t,e,i);}pushRaw(t,e,i,n){let o={_id:a(),eventName:t,eventType:e,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:n??this.identity.getSessionId(),properties:i,context:this.collectContext()},S=this.config.beforeSend,c=S?S(o):o;c&&(this.queue.enqueue(c),this.debugLog("enqueue",c.eventName,c._id),this.queue.size()>=this.config.flushAt&&this.flush());}collectContext(){let t={lib:{name:"@mosovn/echo",version:"0.3.0"},os:{name:Platform.OS,version:String(Platform.Version)}};return this.config.app&&(t.app=this.config.app),t}startFlushTimer(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}installAppStateTracking(){let t=AppState.currentState;this.appStateSub=AppState.addEventListener("change",e=>{e!==t&&(this.debugLog("appstate",t,"\u2192",e),(e==="background"||e==="inactive")&&this.flush(),t=e);});}installErrorTracking(){let t=globalThis.ErrorUtils;if(t){let i=t.getGlobalHandler();t.setGlobalHandler((n,o)=>{this.track("$error",{message:n?.message,stack:n?.stack,fatal:!!o}),i?.(n,o);});}let e=i=>{let n=i?.reason;this.track("$error",{kind:"unhandledrejection",message:n instanceof Error?n.message:String(n),stack:n instanceof Error?n.stack:void 0});};globalThis.addEventListener?.("unhandledrejection",e);}};function X(s){return new v(s)}
2
2
  export{X as createEcho};
package/dist/web.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';function a(){let i=BigInt(Date.now()),e=crypto.getRandomValues(new Uint8Array(10)),t=new Uint8Array(16);t[0]=Number(i>>40n&0xffn),t[1]=Number(i>>32n&0xffn),t[2]=Number(i>>24n&0xffn),t[3]=Number(i>>16n&0xffn),t[4]=Number(i>>8n&0xffn),t[5]=Number(i&0xffn),t[6]=e[0]&15|112,t[7]=e[1],t[8]=e[2]&63|128,t[9]=e[3],t[10]=e[4],t[11]=e[5],t[12]=e[6],t[13]=e[7],t[14]=e[8],t[15]=e[9];let n=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return `${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var f="echo_anonymous_id",w="echo_user_id",d="echo_session_id",h="echo_last_activity";function O(){try{return typeof localStorage>"u"?null:(localStorage.setItem("__echo_probe__","1"),localStorage.removeItem("__echo_probe__"),localStorage)}catch{return null}}function D(){if(typeof location>"u")return null;let i=location.hostname;if(i==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(i))return null;let e=i.split(".");return e.length<2?null:"."+e.slice(-2).join(".")}function N(i){if(typeof document>"u")return null;let e=document.cookie.match(new RegExp("(?:^|; )"+i+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}function P(i,e,t=365){if(typeof document>"u")return;let n=D(),r=new Date(Date.now()+t*864e5).toUTCString(),o=[`${i}=${encodeURIComponent(e)}`,`expires=${r}`,"path=/","SameSite=Lax"];n&&o.push(`domain=${n}`),typeof location<"u"&&location.protocol==="https:"&&o.push("Secure"),document.cookie=o.join("; ");}var m=class{constructor(e){this.storage=O();this.sessionTimeoutMs=e;let t=N(f)||this.storage?.getItem(f)||null;t||(t=a()),this.anonymousId=t,this.persistAnon(),this.userId=this.storage?.getItem(w)||null;let n=Date.now(),r=this.storage?.getItem(d),o=parseInt(this.storage?.getItem(h)||"0",10);r&&n-o<e?this.sessionId=r:(this.sessionId=a(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=n,this.storage?.setItem(h,String(n));}persistAnon(){this.storage?.setItem(f,this.anonymousId),P(f,this.anonymousId);}touch(){let e=Date.now(),t=e-this.lastActivity>=this.sessionTimeoutMs,n;return t&&(n=this.sessionId,this.sessionId=a(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=e,this.storage?.setItem(h,String(e)),{sessionStarted:t,oldSessionId:n}}setUserId(e){this.userId=e,this.storage?.setItem(w,e);}reset(){this.userId=null,this.storage?.removeItem(w),this.anonymousId=a(),this.persistAnon(),this.sessionId=a(),this.storage?.setItem(d,this.sessionId),this.lastActivity=Date.now(),this.storage?.setItem(h,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var p="echo_queue_v1";var g=class{constructor(e){this.buffer=[];this.storage=e==="localstorage"&&typeof localStorage<"u"?localStorage:null,this.restore();}restore(){if(this.storage)try{let e=this.storage.getItem(p);e&&(this.buffer=JSON.parse(e));}catch{this.buffer=[];}}persist(){if(this.storage)try{this.storage.setItem(p,JSON.stringify(this.buffer));}catch{for(;this.buffer.length>50;){this.buffer.shift();try{this.storage.setItem(p,JSON.stringify(this.buffer));break}catch{}}}}enqueue(e){this.buffer.length>=1e3&&this.buffer.shift(),this.buffer.push(e),this.persist();}size(){return this.buffer.length}drain(){let e=this.buffer;return this.buffer=[],this.storage&&this.storage.removeItem(p),e}requeue(e){this.buffer=e.concat(this.buffer).slice(0,1e3),this.persist();}};async function I(i,e){if(i.length===0)return true;let t=e.endpoint.replace(/\/+$/,"")+"/v1/events",n=JSON.stringify({events:i});if(e.useBeacon&&typeof navigator<"u"&&navigator.sendBeacon){let r=`${t}?writeKey=${encodeURIComponent(e.writeKey)}`,o=new Blob([n],{type:"application/json"});return navigator.sendBeacon(r,o)}try{return (await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":e.writeKey},body:n,keepalive:!0,credentials:"omit"})).ok}catch{return false}}var K="https://events.moso.vn",F=50,H=1e4,B=1800*1e3,y=class{constructor(e){this.flushTimer=null;this.flushing=false;this.currentUrl="";this.previousUrl="";if(!e.writeKey)throw new Error("@mosovn/echo: writeKey is required");this.config={writeKey:e.writeKey,endpoint:e.endpoint??K,flushAt:e.flushAt??F,flushInterval:e.flushInterval??H,sessionTimeoutMs:e.sessionTimeoutMs??B,debug:e.debug??false,...e},this.identity=new m(this.config.sessionTimeoutMs);let t=e.storage==="memory"?"memory":"localstorage";this.queue=new g(t),typeof location<"u"&&(this.currentUrl=location.href),typeof document<"u"&&(this.previousUrl=document.referrer||""),this.startFlushTimer(),this.installPageHideFlush();}markPageview(e){e!==this.currentUrl&&(this.previousUrl=this.currentUrl,this.currentUrl=e);}getPreviousUrl(){return this.previousUrl}debugLog(...e){this.config.debug&&console.log("[echo]",...e);}buildEvent(e,t,n){let{sessionStarted:r,oldSessionId:o}=this.identity.touch();return r&&o&&(this.enqueueRaw({_id:a(),eventName:"$session_end",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:o}),this.enqueueRaw({_id:a(),eventName:"$session_start",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId()})),{_id:a(),eventName:e,eventType:t,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId(),properties:n,context:this.collectContext()}}collectContext(){let e={lib:{name:"@mosovn/echo",version:"0.2.0"}};return typeof location<"u"&&(e.page={url:location.href,path:location.pathname,referrer:this.previousUrl,title:typeof document<"u"?document.title:""}),typeof navigator<"u"&&(e.locale=navigator.language),typeof screen<"u"&&(e.screen={width:screen.width,height:screen.height}),e}enqueueRaw(e){let t=this.config.beforeSend,n=t?t(e):e;n&&(this.queue.enqueue(n),this.debugLog("enqueue",n.eventName,n._id),this.queue.size()>=this.config.flushAt&&this.flush());}enqueueEvent(e,t,n){let r=this.buildEvent(e,t,n);this.enqueueRaw(r);}track(e,t){this.enqueueEvent(e,"custom",t);}identify(e,t){this.identity.setUserId(e),this.enqueueEvent("$identify","auto",t),fetch(this.config.endpoint.replace(/\/+$/,"")+"/v1/identify",{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":this.config.writeKey},body:JSON.stringify({userId:e,anonymousId:this.identity.getAnonymousId(),traits:t||{}}),keepalive:true,credentials:"omit"}).catch(()=>{});}setUserProperties(e){let t=this.identity.getUserId();t?this.identify(t,e):this.enqueueEvent("$set_user_properties","auto",e);}page(e){this.enqueueEvent("$pageview","auto",e);}reset(){this.identity.reset(),this.queue.drain();}async flush(){if(this.flushing||this.queue.size()===0)return;this.flushing=true;let e=this.queue.drain();await I(e,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",e.length):(this.queue.requeue(e),this.debugLog("flush failed, requeued",e.length)),this.flushing=false;}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.identity.getSessionId()}startFlushTimer(){typeof setInterval>"u"||(this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval));}installPageHideFlush(){if(typeof document>"u")return;let e=()=>{let t=this.queue.drain();t.length!==0&&I(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint,useBeacon:true});};document.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&e();});}};var j=["input","textarea","select","[data-private]"],V=["[data-no-track]","script","style"];function T(i,e){for(let t of e)try{if(i.matches(t))return !0}catch{}return false}function l(i,e){let t=e.blockSelectors&&e.blockSelectors.length?e.blockSelectors:V;return T(i,t)}function Y(i,e){let t=e.maskTextSelectors&&e.maskTextSelectors.length?e.maskTextSelectors:j;return T(i,t)}function J(i,e){return Y(i,e)?"":(i.innerText||i.textContent||"").replace(/\s+/g," ").trim().slice(0,200)}function S(i){let e=[],t=i,n=0;for(;t&&t.nodeType===1&&n<6;){let r=t.tagName.toLowerCase();if(t.id){e.unshift(`${r}#${t.id}`);break}let o=(t.getAttribute("class")||"").split(/\s+/).filter(Boolean).slice(0,2).map(u=>"."+u).join(""),s="";if(t.parentElement){let u=Array.from(t.parentElement.children).filter(c=>c.tagName===t.tagName);u.length>1&&(s=`:nth-of-type(${u.indexOf(t)+1})`);}e.unshift(`${r}${o}${s}`),t=t.parentElement,n++;}return e.join(" > ")}function E(i,e){let t=i.tagName.toLowerCase(),n={selector:S(i),tag:t,text:J(i,e)},r=i.id;r&&(n.id=r);let o=i.getAttribute&&i.getAttribute("name");if(o&&(n.name=o),t==="a"){let u=i.href;u&&(n.href=u);}let s=i.getAttribute&&i.getAttribute("aria-label");return s&&(n.ariaLabel=s),n}function v(i){let e=i.tagName?.toLowerCase();if(!e)return false;if(e==="a"||e==="button")return true;let t=i.getAttribute&&i.getAttribute("role");return t==="button"||t==="link"||t==="tab"||t==="menuitem"}function _(i,e,t){typeof document>"u"||document.addEventListener("click",n=>{let r=n.target;if(r){if(e==="instrumented"){let o=r;for(;o&&o!==document.body&&!v(o);)o=o.parentElement;if(!o||o===document.body)return;r=o;}l(r,t)||i.enqueueEvent("$click","auto",E(r,t));}},true);}function k(i){if(typeof window>"u")return;let e=[],t=n=>{let r=Date.now();for(;e.length&&r-e[0].at>5e3;)e.shift();return e.some(o=>o.key===n)?false:(e.push({key:n,at:r}),true)};window.addEventListener("error",n=>{let r=n.message||String(n.error||"unknown"),o=`${r}|${n.filename}|${n.lineno}`;t(o)&&i.enqueueEvent("$error","auto",{message:r,source:n.filename,line:n.lineno,column:n.colno,stack:n.error&&n.error.stack||void 0});}),window.addEventListener("unhandledrejection",n=>{let r=n.reason,o=r instanceof Error?r.message:String(r),s=r instanceof Error?r.stack:void 0,u=`unhandled:${o}`;t(u)&&i.enqueueEvent("$error","auto",{kind:"unhandledrejection",message:o,stack:s});});}function A(i,e){typeof document>"u"||document.addEventListener("submit",t=>{let n=t.target;if(!n||n.tagName!=="FORM"||l(n,e))return;let r=n.querySelectorAll("input, select, textarea").length;i.enqueueEvent("$form_submit","auto",{selector:S(n),id:n.id||void 0,name:n.getAttribute("name")||void 0,action:n.getAttribute("action")||void 0,method:(n.getAttribute("method")||"get").toUpperCase(),fieldCount:r});},true);}var X=500;function x(i,e){if(typeof document>"u")return;let t=null,n=0;document.addEventListener("mouseover",r=>{let o=r.target;if(!o)return;let s=o;for(;s&&s!==document.body&&!v(s);)s=s.parentElement;if(!s||s===document.body||s===t||l(s,e))return;let u=Date.now();if(u-n<X){t=s;return}t=s,n=u,i.enqueueEvent("$hover","auto",E(s,e));},true);}function R(i){if(typeof window>"u"||typeof history>"u")return;let e=location.href,t=0,n=()=>{let o=Date.now();o-t<100||location.href===e&&t!==0||(e=location.href,t=o,i.markPageview(location.href),i.enqueueEvent("$pageview","auto",{url:location.href,path:location.pathname,title:document?.title,referrer:i.getPreviousUrl()}));};n();let r=o=>{let s=history[o];history[o]=function(...u){let c=s.apply(history,u);return n(),c};};r("pushState"),r("replaceState"),window.addEventListener("popstate",n),window.addEventListener("hashchange",n);}function U(i){if(typeof window>"u"||typeof document>"u")return;let e=0,t=0,n=location.href,r=()=>{location.href!==n&&(n=location.href,e=0,t=0);let o=Date.now();if(o-t<250)return;let s=document.documentElement,u=window.scrollY||s.scrollTop||0,c=window.innerHeight||s.clientHeight,C=s.scrollHeight-c;if(C<=0)return;let b=Math.max(0,Math.min(100,Math.round(u/C*100)));b-e<10||(e=b,t=o,i.enqueueEvent("$scroll","auto",{percent:b,pixels:u}));};window.addEventListener("scroll",r,{passive:true});}function L(i){if(typeof document>"u")return;let e=document.visibilityState;document.addEventListener("visibilitychange",()=>{let t=document.visibilityState;t!==e&&(e=t,i.enqueueEvent("$visibility","auto",{state:t}));});}function M(i){if(typeof PerformanceObserver>"u")return;let e=(t,n,r)=>{i.enqueueEvent("$web_vital","auto",{name:t,value:n,...r});};try{new PerformanceObserver(n=>{let r=n.getEntries(),o=r[r.length-1];o&&e("LCP",Math.round(o.startTime));}).observe({type:"largest-contentful-paint",buffered:!0});}catch{}try{let t=0;new PerformanceObserver(r=>{for(let o of r.getEntries())o.hadRecentInput||(t+=o.value);}).observe({type:"layout-shift",buffered:!0}),addEventListener("pagehide",()=>e("CLS",Math.round(t*1e3)/1e3));}catch{}try{new PerformanceObserver(n=>{let r=n.getEntries()[0];r&&e("FID",Math.round(r.processingStart-r.startTime));}).observe({type:"first-input",buffered:!0});}catch{}}var q={pageviews:true,clicks:"all",forms:true,scroll:true,visibility:true,mouseover:true,errors:true,vitals:true,sessions:true};function Q(i){return i===false?null:i===void 0||i===true?q:{...q,...i}}function $(i,e){let t=Q(e.autoCapture);if(!t)return;let n={maskTextSelectors:e.maskTextSelectors,blockSelectors:e.blockSelectors};if(t.pageviews&&R(i),t.clicks){let r=t.clicks==="instrumented"?"instrumented":"all";_(i,r,n);}t.forms&&A(i,n),t.scroll&&U(i),t.visibility&&L(i),t.mouseover&&x(i,n),t.errors&&k(i),t.vitals&&M(i);}function xe(i){let e=new y(i);return $(e,i),e}
2
- exports.createEcho=xe;
1
+ 'use strict';function u(){let n=BigInt(Date.now()),e=crypto.getRandomValues(new Uint8Array(10)),t=new Uint8Array(16);t[0]=Number(n>>40n&0xffn),t[1]=Number(n>>32n&0xffn),t[2]=Number(n>>24n&0xffn),t[3]=Number(n>>16n&0xffn),t[4]=Number(n>>8n&0xffn),t[5]=Number(n&0xffn),t[6]=e[0]&15|112,t[7]=e[1],t[8]=e[2]&63|128,t[9]=e[3],t[10]=e[4],t[11]=e[5],t[12]=e[6],t[13]=e[7],t[14]=e[8],t[15]=e[9];let i=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return `${i.slice(0,8)}-${i.slice(8,12)}-${i.slice(12,16)}-${i.slice(16,20)}-${i.slice(20)}`}var f="echo_anonymous_id",I="echo_user_id",d="echo_session_id",h="echo_last_activity";function X(){try{return typeof localStorage>"u"?null:(localStorage.setItem("__echo_probe__","1"),localStorage.removeItem("__echo_probe__"),localStorage)}catch{return null}}function W(){if(typeof location>"u")return null;let n=location.hostname;if(n==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(n))return null;let e=n.split(".");return e.length<2?null:"."+e.slice(-2).join(".")}function Q(n){if(typeof document>"u")return null;let e=document.cookie.match(new RegExp("(?:^|; )"+n+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}function z(n,e,t=365){if(typeof document>"u")return;let i=W(),r=new Date(Date.now()+t*864e5).toUTCString(),o=[`${n}=${encodeURIComponent(e)}`,`expires=${r}`,"path=/","SameSite=Lax"];i&&o.push(`domain=${i}`),typeof location<"u"&&location.protocol==="https:"&&o.push("Secure"),document.cookie=o.join("; ");}var m=class{constructor(e){this.storage=X();this.sessionTimeoutMs=e;let t=Q(f)||this.storage?.getItem(f)||null;t||(t=u()),this.anonymousId=t,this.persistAnon(),this.userId=this.storage?.getItem(I)||null;let i=Date.now(),r=this.storage?.getItem(d),o=parseInt(this.storage?.getItem(h)||"0",10);r&&i-o<e?this.sessionId=r:(this.sessionId=u(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=i,this.storage?.setItem(h,String(i));}persistAnon(){this.storage?.setItem(f,this.anonymousId),z(f,this.anonymousId);}touch(){let e=Date.now(),t=e-this.lastActivity>=this.sessionTimeoutMs,i;return t&&(i=this.sessionId,this.sessionId=u(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=e,this.storage?.setItem(h,String(e)),{sessionStarted:t,oldSessionId:i}}setUserId(e){this.userId=e,this.storage?.setItem(I,e);}reset(){this.userId=null,this.storage?.removeItem(I),this.anonymousId=u(),this.persistAnon(),this.sessionId=u(),this.storage?.setItem(d,this.sessionId),this.lastActivity=Date.now(),this.storage?.setItem(h,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var p="echo_queue_v1";var g=class{constructor(e){this.buffer=[];this.storage=e==="localstorage"&&typeof localStorage<"u"?localStorage:null,this.restore();}restore(){if(this.storage)try{let e=this.storage.getItem(p);e&&(this.buffer=JSON.parse(e));}catch{this.buffer=[];}}persist(){if(this.storage)try{this.storage.setItem(p,JSON.stringify(this.buffer));}catch{for(;this.buffer.length>50;){this.buffer.shift();try{this.storage.setItem(p,JSON.stringify(this.buffer));break}catch{}}}}enqueue(e){this.buffer.length>=1e3&&this.buffer.shift(),this.buffer.push(e),this.persist();}size(){return this.buffer.length}drain(){let e=this.buffer;return this.buffer=[],this.storage&&this.storage.removeItem(p),e}requeue(e){this.buffer=e.concat(this.buffer).slice(0,1e3),this.persist();}};async function C(n,e){if(n.length===0)return true;let t=e.endpoint.replace(/\/+$/,"")+"/v1/events",i=JSON.stringify({events:n});if(e.useBeacon&&typeof navigator<"u"&&navigator.sendBeacon){let r=`${t}?writeKey=${encodeURIComponent(e.writeKey)}`,o=new Blob([i],{type:"application/json"});return navigator.sendBeacon(r,o)}try{return (await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":e.writeKey},body:i,keepalive:!0,credentials:"omit"})).ok}catch{return false}}var y="echo_campaign_first",_="echo_campaign_last",G=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","utm_id"],Z=["gclid","gbraid","wbraid","dclid","fbclid","msclkid","ttclid","twclid","li_fat_id","igshid","yclid","epik","irclickid"];function U(n){let e={},t;try{t=new URL(n).search;}catch{return e}if(!t)return e;let i=new URLSearchParams(t);for(let r of G){let o=i.get(r);o&&(e[r]=o);}for(let r of Z){let o=i.get(r);o&&(e[r]=o);}return e}function k(n){try{let e=localStorage.getItem(n);return e?JSON.parse(e):null}catch{return null}}function A(n,e){try{localStorage.setItem(n,JSON.stringify(e));}catch{}}function T(){if(typeof location>"u"||typeof localStorage>"u")return;let n=U(location.href);if(Object.keys(n).length===0)return;let e={...n,_landedAt:new Date().toISOString(),_landingUrl:location.href,_referrer:typeof document<"u"?document.referrer:""};k(y)||A(y,e),A(_,e);}function L(){let n=typeof location<"u"?U(location.href):{},e=typeof localStorage<"u"?k(y):null,t=typeof localStorage<"u"?k(_):null,i={campaign:n};return e&&(i.campaign_first=e),t&&(i.campaign_last=t),i}function M(){try{localStorage.removeItem(y),localStorage.removeItem(_);}catch{}}var ee="https://events.moso.vn",te=50,ne=1e4,ie=1800*1e3,v=class{constructor(e){this.flushTimer=null;this.flushing=false;this.currentUrl="";this.previousUrl="";if(!e.writeKey)throw new Error("@mosovn/echo: writeKey is required");this.config={writeKey:e.writeKey,endpoint:e.endpoint??ee,flushAt:e.flushAt??te,flushInterval:e.flushInterval??ne,sessionTimeoutMs:e.sessionTimeoutMs??ie,debug:e.debug??false,...e},this.identity=new m(this.config.sessionTimeoutMs);let t=e.storage==="memory"?"memory":"localstorage";this.queue=new g(t),typeof location<"u"&&(this.currentUrl=location.href),typeof document<"u"&&(this.previousUrl=document.referrer||""),T(),this.startFlushTimer(),this.installPageHideFlush();}markPageview(e){e!==this.currentUrl&&(this.previousUrl=this.currentUrl,this.currentUrl=e,T());}getPreviousUrl(){return this.previousUrl}debugLog(...e){this.config.debug&&console.log("[echo]",...e);}buildEvent(e,t,i){let{sessionStarted:r,oldSessionId:o}=this.identity.touch();return r&&o&&(this.enqueueRaw({_id:u(),eventName:"$session_end",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:o}),this.enqueueRaw({_id:u(),eventName:"$session_start",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId()})),{_id:u(),eventName:e,eventType:t,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId(),properties:i,context:this.collectContext()}}collectContext(){let e={lib:{name:"@mosovn/echo",version:"0.3.0"}};return typeof location<"u"&&(e.page={url:location.href,path:location.pathname,referrer:this.previousUrl,title:typeof document<"u"?document.title:""}),typeof navigator<"u"&&(e.locale=navigator.language),typeof screen<"u"&&(e.screen={width:screen.width,height:screen.height}),Object.assign(e,L()),e}enqueueRaw(e){let t=this.config.beforeSend,i=t?t(e):e;i&&(this.queue.enqueue(i),this.debugLog("enqueue",i.eventName,i._id),this.queue.size()>=this.config.flushAt&&this.flush());}enqueueEvent(e,t,i){let r=this.buildEvent(e,t,i);this.enqueueRaw(r);}track(e,t){this.enqueueEvent(e,"custom",t);}identify(e,t){this.identity.setUserId(e),this.enqueueEvent("$identify","auto",t),fetch(this.config.endpoint.replace(/\/+$/,"")+"/v1/identify",{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":this.config.writeKey},body:JSON.stringify({userId:e,anonymousId:this.identity.getAnonymousId(),traits:t||{}}),keepalive:true,credentials:"omit"}).catch(()=>{});}setUserProperties(e){let t=this.identity.getUserId();t?this.identify(t,e):this.enqueueEvent("$set_user_properties","auto",e);}page(e){this.enqueueEvent("$pageview","auto",e);}reset(){this.identity.reset(),this.queue.drain(),M();}async flush(){if(this.flushing||this.queue.size()===0)return;this.flushing=true;let e=this.queue.drain();await C(e,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",e.length):(this.queue.requeue(e),this.debugLog("flush failed, requeued",e.length)),this.flushing=false;}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.identity.getSessionId()}startFlushTimer(){typeof setInterval>"u"||(this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval));}installPageHideFlush(){if(typeof document>"u")return;let e=()=>{let t=this.queue.drain();t.length!==0&&C(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint,useBeacon:true});};document.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&e();});}};var re=["input","textarea","select","[data-private]"],oe=["[data-no-track]","script","style"];function O(n,e){for(let t of e)try{if(n.matches(t))return !0}catch{}return false}function l(n,e){let t=e.blockSelectors&&e.blockSelectors.length?e.blockSelectors:oe;return O(n,t)}function se(n,e){let t=e.maskTextSelectors&&e.maskTextSelectors.length?e.maskTextSelectors:re;return O(n,t)}function ae(n,e){return se(n,e)?"":(n.innerText||n.textContent||"").replace(/\s+/g," ").trim().slice(0,200)}function R(n){let e=[],t=n,i=0;for(;t&&t.nodeType===1&&i<6;){let r=t.tagName.toLowerCase();if(t.id){e.unshift(`${r}#${t.id}`);break}let o=(t.getAttribute("class")||"").split(/\s+/).filter(Boolean).slice(0,2).map(a=>"."+a).join(""),s="";if(t.parentElement){let a=Array.from(t.parentElement.children).filter(c=>c.tagName===t.tagName);a.length>1&&(s=`:nth-of-type(${a.indexOf(t)+1})`);}e.unshift(`${r}${o}${s}`),t=t.parentElement,i++;}return e.join(" > ")}function b(n,e){let t=n.tagName.toLowerCase(),i={selector:R(n),tag:t,text:ae(n,e)},r=n.id;r&&(i.id=r);let o=n.getAttribute&&n.getAttribute("name");if(o&&(i.name=o),t==="a"){let a=n.href;a&&(i.href=a);}let s=n.getAttribute&&n.getAttribute("aria-label");return s&&(i.ariaLabel=s),i}function E(n){let e=n.tagName?.toLowerCase();if(!e)return false;if(e==="a"||e==="button")return true;let t=n.getAttribute&&n.getAttribute("role");return t==="button"||t==="link"||t==="tab"||t==="menuitem"}function q(n,e,t){typeof document>"u"||document.addEventListener("click",i=>{let r=i.target;if(r){if(e==="instrumented"){let o=r;for(;o&&o!==document.body&&!E(o);)o=o.parentElement;if(!o||o===document.body)return;r=o;}l(r,t)||n.enqueueEvent("$click","auto",b(r,t));}},true);}function $(n){if(typeof window>"u")return;let e=[],t=i=>{let r=Date.now();for(;e.length&&r-e[0].at>5e3;)e.shift();return e.some(o=>o.key===i)?false:(e.push({key:i,at:r}),true)};window.addEventListener("error",i=>{let r=i.message||String(i.error||"unknown"),o=`${r}|${i.filename}|${i.lineno}`;t(o)&&n.enqueueEvent("$error","auto",{message:r,source:i.filename,line:i.lineno,column:i.colno,stack:i.error&&i.error.stack||void 0});}),window.addEventListener("unhandledrejection",i=>{let r=i.reason,o=r instanceof Error?r.message:String(r),s=r instanceof Error?r.stack:void 0,a=`unhandled:${o}`;t(a)&&n.enqueueEvent("$error","auto",{kind:"unhandledrejection",message:o,stack:s});});}function D(n,e){typeof document>"u"||document.addEventListener("submit",t=>{let i=t.target;if(!i||i.tagName!=="FORM"||l(i,e))return;let r=i.querySelectorAll("input, select, textarea").length;n.enqueueEvent("$form_submit","auto",{selector:R(i),id:i.id||void 0,name:i.getAttribute("name")||void 0,action:i.getAttribute("action")||void 0,method:(i.getAttribute("method")||"get").toUpperCase(),fieldCount:r});},true);}var ue=500;function P(n,e){if(typeof document>"u")return;let t=null,i=0;document.addEventListener("mouseover",r=>{let o=r.target;if(!o)return;let s=o;for(;s&&s!==document.body&&!E(s);)s=s.parentElement;if(!s||s===document.body||s===t||l(s,e))return;let a=Date.now();if(a-i<ue){t=s;return}t=s,i=a,n.enqueueEvent("$hover","auto",b(s,e));},true);}function K(n){if(typeof window>"u"||typeof history>"u")return;let e=location.href,t=0,i=()=>{let o=Date.now();o-t<100||location.href===e&&t!==0||(e=location.href,t=o,n.markPageview(location.href),n.enqueueEvent("$pageview","auto",{url:location.href,path:location.pathname,title:document?.title,referrer:n.getPreviousUrl()}));};i();let r=o=>{let s=history[o];history[o]=function(...a){let c=s.apply(history,a);return i(),c};};r("pushState"),r("replaceState"),window.addEventListener("popstate",i),window.addEventListener("hashchange",i);}function N(n){if(typeof window>"u"||typeof document>"u")return;let e=0,t=0,i=location.href,r=()=>{location.href!==i&&(i=location.href,e=0,t=0);let o=Date.now();if(o-t<250)return;let s=document.documentElement,a=window.scrollY||s.scrollTop||0,c=window.innerHeight||s.clientHeight,x=s.scrollHeight-c;if(x<=0)return;let S=Math.max(0,Math.min(100,Math.round(a/x*100)));S-e<10||(e=S,t=o,n.enqueueEvent("$scroll","auto",{percent:S,pixels:a}));};window.addEventListener("scroll",r,{passive:true});}function F(n){if(typeof document>"u")return;let e=document.visibilityState;document.addEventListener("visibilitychange",()=>{let t=document.visibilityState;t!==e&&(e=t,n.enqueueEvent("$visibility","auto",{state:t}));});}function B(n){if(typeof PerformanceObserver>"u")return;let e=(t,i,r)=>{n.enqueueEvent("$web_vital","auto",{name:t,value:i,...r});};try{new PerformanceObserver(i=>{let r=i.getEntries(),o=r[r.length-1];o&&e("LCP",Math.round(o.startTime));}).observe({type:"largest-contentful-paint",buffered:!0});}catch{}try{let t=0;new PerformanceObserver(r=>{for(let o of r.getEntries())o.hadRecentInput||(t+=o.value);}).observe({type:"layout-shift",buffered:!0}),addEventListener("pagehide",()=>e("CLS",Math.round(t*1e3)/1e3));}catch{}try{new PerformanceObserver(i=>{let r=i.getEntries()[0];r&&e("FID",Math.round(r.processingStart-r.startTime));}).observe({type:"first-input",buffered:!0});}catch{}}var H={pageviews:true,clicks:"all",forms:true,scroll:true,visibility:true,mouseover:true,errors:true,vitals:true,sessions:true};function le(n){return n===false?null:n===void 0||n===true?H:{...H,...n}}function j(n,e){let t=le(e.autoCapture);if(!t)return;let i={maskTextSelectors:e.maskTextSelectors,blockSelectors:e.blockSelectors};if(t.pageviews&&K(n),t.clicks){let r=t.clicks==="instrumented"?"instrumented":"all";q(n,r,i);}t.forms&&D(n,i),t.scroll&&N(n),t.visibility&&F(n),t.mouseover&&P(n,i),t.errors&&$(n),t.vitals&&B(n);}function ce(n){let e=3735928559,t=1103547991;for(let r=0;r<n.length;r++){let o=n.charCodeAt(r);e=Math.imul(e^o,2654435761),t=Math.imul(t^o,1597334677);}return e=Math.imul(e^e>>>16,2246822507)^Math.imul(t^t>>>13,3266489909),t=Math.imul(t^t>>>16,2246822507)^Math.imul(e^e>>>13,3266489909),4294967296*(2097151&t)+(e>>>0)}function Y(n,e){return e<=0?false:e>=1?true:ce(n)%1e4/1e4<e}var w=class{constructor(e,t,i){this.client=e;this.intervalMs=t;this.seq=0;this.buffer=[];this.bytesPending=0;this.timer=null;this.endpoint=`${e.config.endpoint}${i}`,this.writeKey=e.config.writeKey,this.sessionId=e.identity.getSessionId(),this.timer=setInterval(()=>{this.flush();},t),typeof window<"u"&&(window.addEventListener("pagehide",()=>{this.flush(true);},{capture:true}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush(true);}));}push(e){this.buffer.push(e),this.bytesPending+=JSON.stringify(e).length,this.bytesPending>=5e5&&this.flush();}stop(){this.timer&&clearInterval(this.timer),this.timer=null;}async flush(e=false){if(this.buffer.length===0)return;let t=this.buffer.splice(0);this.bytesPending=0;let i=JSON.stringify({sessionId:this.sessionId,anonymousId:this.client.identity.getAnonymousId(),userId:this.client.identity.getUserId(),seq:this.seq++,events:t,ua:typeof navigator<"u"?navigator.userAgent:"",viewport:typeof window<"u"?{w:window.innerWidth,h:window.innerHeight}:void 0}),r=`${this.endpoint}?writeKey=${encodeURIComponent(this.writeKey)}`;if(e&&typeof navigator<"u"&&navigator.sendBeacon){try{navigator.sendBeacon(r,new Blob([i],{type:"application/json"}));}catch{}return}try{await fetch(r,{method:"POST",keepalive:!0,headers:{"X-Echo-Key":this.writeKey,"Content-Type":"application/json"},body:i});}catch(o){this.client.config.debug&&console.warn("[echo:replay] chunk upload failed",o);}}};var J={enabled:false,sampleRate:.1,maskTextSelectors:[],blockSelectors:[],blockUrlPatterns:[],chunkIntervalMs:3e4,chunkPath:"/v1/replays/chunk"};function fe(n){if(!n)return null;if(n===true)return {...J,enabled:true};let e={...J,...n,enabled:n.enabled??true};return e.enabled?e:null}function de(n){return typeof location>"u"?false:n.some(e=>typeof e=="string"?location.pathname.startsWith(e):e.test(location.pathname))}async function V(n){if(typeof window>"u"||typeof document>"u")return;let e=fe(n.config.replay);if(!e)return;if(de(e.blockUrlPatterns)){n.config.debug&&console.log("[echo:replay] blocked by URL pattern",location.pathname);return}let t=n.identity.getSessionId();if(!Y(t,e.sampleRate)){n.config.debug&&console.log("[echo:replay] session not sampled",t);return}let i;try{i=await import('rrweb');}catch(o){n.config.debug&&console.warn("[echo:replay] rrweb not available",o);return}let r=new w(n,e.chunkIntervalMs,e.chunkPath);i.record({emit:o=>r.push(o),maskTextSelector:e.maskTextSelectors.length?e.maskTextSelectors.join(","):void 0,blockSelector:e.blockSelectors.length?e.blockSelectors.join(","):void 0,maskInputOptions:{password:true,email:true,tel:true,date:true},recordCanvas:false,sampling:{mousemove:50,scroll:150,input:"last"}}),n.config.debug&&console.log("[echo:replay] recording session",t);}function et(n){let e=new v(n);return j(e,n),V(e),e}
2
+ exports.createEcho=et;
package/dist/web.d.cts CHANGED
@@ -36,6 +36,27 @@ interface EchoConfig {
36
36
  trackSessions?: boolean;
37
37
  /** [Native only] Track JS errors via ErrorUtils + unhandledrejection. Default true. */
38
38
  trackErrors?: boolean;
39
+ /** [Web only] Session replay configuration. Default OFF (replay is opt-in). */
40
+ replay?: ReplayConfig | boolean;
41
+ }
42
+ interface ReplayConfig {
43
+ /** Master toggle. If config is passed without this set, defaults to true. */
44
+ enabled?: boolean;
45
+ /**
46
+ * Fraction of sessions to record [0..1]. Decision is sticky per sessionId,
47
+ * so a recorded user stays recorded for the whole session. Default 0.1.
48
+ */
49
+ sampleRate?: number;
50
+ /** CSS selectors whose text is masked as ▓▓▓ in replay. */
51
+ maskTextSelectors?: string[];
52
+ /** CSS selectors whose subtree is fully removed from replay. */
53
+ blockSelectors?: string[];
54
+ /** URL path regexes/prefixes to NEVER record (vd /reset-password). */
55
+ blockUrlPatterns?: (string | RegExp)[];
56
+ /** Chunk flush interval in ms. Default 30000. */
57
+ chunkIntervalMs?: number;
58
+ /** Endpoint path. Default '/v1/replays/chunk'. */
59
+ chunkPath?: string;
39
60
  }
40
61
  interface AutoCaptureConfig {
41
62
  pageviews?: boolean;
@@ -98,4 +119,4 @@ interface EchoClient {
98
119
 
99
120
  declare function createEcho(config: EchoConfig): EchoClient;
100
121
 
101
- export { type AutoCaptureConfig, type EchoClient, type EchoConfig, type EchoEvent, createEcho };
122
+ export { type AutoCaptureConfig, type EchoClient, type EchoConfig, type EchoEvent, type ReplayConfig, createEcho };
package/dist/web.d.ts CHANGED
@@ -36,6 +36,27 @@ interface EchoConfig {
36
36
  trackSessions?: boolean;
37
37
  /** [Native only] Track JS errors via ErrorUtils + unhandledrejection. Default true. */
38
38
  trackErrors?: boolean;
39
+ /** [Web only] Session replay configuration. Default OFF (replay is opt-in). */
40
+ replay?: ReplayConfig | boolean;
41
+ }
42
+ interface ReplayConfig {
43
+ /** Master toggle. If config is passed without this set, defaults to true. */
44
+ enabled?: boolean;
45
+ /**
46
+ * Fraction of sessions to record [0..1]. Decision is sticky per sessionId,
47
+ * so a recorded user stays recorded for the whole session. Default 0.1.
48
+ */
49
+ sampleRate?: number;
50
+ /** CSS selectors whose text is masked as ▓▓▓ in replay. */
51
+ maskTextSelectors?: string[];
52
+ /** CSS selectors whose subtree is fully removed from replay. */
53
+ blockSelectors?: string[];
54
+ /** URL path regexes/prefixes to NEVER record (vd /reset-password). */
55
+ blockUrlPatterns?: (string | RegExp)[];
56
+ /** Chunk flush interval in ms. Default 30000. */
57
+ chunkIntervalMs?: number;
58
+ /** Endpoint path. Default '/v1/replays/chunk'. */
59
+ chunkPath?: string;
39
60
  }
40
61
  interface AutoCaptureConfig {
41
62
  pageviews?: boolean;
@@ -98,4 +119,4 @@ interface EchoClient {
98
119
 
99
120
  declare function createEcho(config: EchoConfig): EchoClient;
100
121
 
101
- export { type AutoCaptureConfig, type EchoClient, type EchoConfig, type EchoEvent, createEcho };
122
+ export { type AutoCaptureConfig, type EchoClient, type EchoConfig, type EchoEvent, type ReplayConfig, createEcho };
package/dist/web.js CHANGED
@@ -1,2 +1,2 @@
1
- function a(){let i=BigInt(Date.now()),e=crypto.getRandomValues(new Uint8Array(10)),t=new Uint8Array(16);t[0]=Number(i>>40n&0xffn),t[1]=Number(i>>32n&0xffn),t[2]=Number(i>>24n&0xffn),t[3]=Number(i>>16n&0xffn),t[4]=Number(i>>8n&0xffn),t[5]=Number(i&0xffn),t[6]=e[0]&15|112,t[7]=e[1],t[8]=e[2]&63|128,t[9]=e[3],t[10]=e[4],t[11]=e[5],t[12]=e[6],t[13]=e[7],t[14]=e[8],t[15]=e[9];let n=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return `${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var f="echo_anonymous_id",w="echo_user_id",d="echo_session_id",h="echo_last_activity";function O(){try{return typeof localStorage>"u"?null:(localStorage.setItem("__echo_probe__","1"),localStorage.removeItem("__echo_probe__"),localStorage)}catch{return null}}function D(){if(typeof location>"u")return null;let i=location.hostname;if(i==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(i))return null;let e=i.split(".");return e.length<2?null:"."+e.slice(-2).join(".")}function N(i){if(typeof document>"u")return null;let e=document.cookie.match(new RegExp("(?:^|; )"+i+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}function P(i,e,t=365){if(typeof document>"u")return;let n=D(),r=new Date(Date.now()+t*864e5).toUTCString(),o=[`${i}=${encodeURIComponent(e)}`,`expires=${r}`,"path=/","SameSite=Lax"];n&&o.push(`domain=${n}`),typeof location<"u"&&location.protocol==="https:"&&o.push("Secure"),document.cookie=o.join("; ");}var m=class{constructor(e){this.storage=O();this.sessionTimeoutMs=e;let t=N(f)||this.storage?.getItem(f)||null;t||(t=a()),this.anonymousId=t,this.persistAnon(),this.userId=this.storage?.getItem(w)||null;let n=Date.now(),r=this.storage?.getItem(d),o=parseInt(this.storage?.getItem(h)||"0",10);r&&n-o<e?this.sessionId=r:(this.sessionId=a(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=n,this.storage?.setItem(h,String(n));}persistAnon(){this.storage?.setItem(f,this.anonymousId),P(f,this.anonymousId);}touch(){let e=Date.now(),t=e-this.lastActivity>=this.sessionTimeoutMs,n;return t&&(n=this.sessionId,this.sessionId=a(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=e,this.storage?.setItem(h,String(e)),{sessionStarted:t,oldSessionId:n}}setUserId(e){this.userId=e,this.storage?.setItem(w,e);}reset(){this.userId=null,this.storage?.removeItem(w),this.anonymousId=a(),this.persistAnon(),this.sessionId=a(),this.storage?.setItem(d,this.sessionId),this.lastActivity=Date.now(),this.storage?.setItem(h,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var p="echo_queue_v1";var g=class{constructor(e){this.buffer=[];this.storage=e==="localstorage"&&typeof localStorage<"u"?localStorage:null,this.restore();}restore(){if(this.storage)try{let e=this.storage.getItem(p);e&&(this.buffer=JSON.parse(e));}catch{this.buffer=[];}}persist(){if(this.storage)try{this.storage.setItem(p,JSON.stringify(this.buffer));}catch{for(;this.buffer.length>50;){this.buffer.shift();try{this.storage.setItem(p,JSON.stringify(this.buffer));break}catch{}}}}enqueue(e){this.buffer.length>=1e3&&this.buffer.shift(),this.buffer.push(e),this.persist();}size(){return this.buffer.length}drain(){let e=this.buffer;return this.buffer=[],this.storage&&this.storage.removeItem(p),e}requeue(e){this.buffer=e.concat(this.buffer).slice(0,1e3),this.persist();}};async function I(i,e){if(i.length===0)return true;let t=e.endpoint.replace(/\/+$/,"")+"/v1/events",n=JSON.stringify({events:i});if(e.useBeacon&&typeof navigator<"u"&&navigator.sendBeacon){let r=`${t}?writeKey=${encodeURIComponent(e.writeKey)}`,o=new Blob([n],{type:"application/json"});return navigator.sendBeacon(r,o)}try{return (await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":e.writeKey},body:n,keepalive:!0,credentials:"omit"})).ok}catch{return false}}var K="https://events.moso.vn",F=50,H=1e4,B=1800*1e3,y=class{constructor(e){this.flushTimer=null;this.flushing=false;this.currentUrl="";this.previousUrl="";if(!e.writeKey)throw new Error("@mosovn/echo: writeKey is required");this.config={writeKey:e.writeKey,endpoint:e.endpoint??K,flushAt:e.flushAt??F,flushInterval:e.flushInterval??H,sessionTimeoutMs:e.sessionTimeoutMs??B,debug:e.debug??false,...e},this.identity=new m(this.config.sessionTimeoutMs);let t=e.storage==="memory"?"memory":"localstorage";this.queue=new g(t),typeof location<"u"&&(this.currentUrl=location.href),typeof document<"u"&&(this.previousUrl=document.referrer||""),this.startFlushTimer(),this.installPageHideFlush();}markPageview(e){e!==this.currentUrl&&(this.previousUrl=this.currentUrl,this.currentUrl=e);}getPreviousUrl(){return this.previousUrl}debugLog(...e){this.config.debug&&console.log("[echo]",...e);}buildEvent(e,t,n){let{sessionStarted:r,oldSessionId:o}=this.identity.touch();return r&&o&&(this.enqueueRaw({_id:a(),eventName:"$session_end",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:o}),this.enqueueRaw({_id:a(),eventName:"$session_start",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId()})),{_id:a(),eventName:e,eventType:t,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId(),properties:n,context:this.collectContext()}}collectContext(){let e={lib:{name:"@mosovn/echo",version:"0.2.0"}};return typeof location<"u"&&(e.page={url:location.href,path:location.pathname,referrer:this.previousUrl,title:typeof document<"u"?document.title:""}),typeof navigator<"u"&&(e.locale=navigator.language),typeof screen<"u"&&(e.screen={width:screen.width,height:screen.height}),e}enqueueRaw(e){let t=this.config.beforeSend,n=t?t(e):e;n&&(this.queue.enqueue(n),this.debugLog("enqueue",n.eventName,n._id),this.queue.size()>=this.config.flushAt&&this.flush());}enqueueEvent(e,t,n){let r=this.buildEvent(e,t,n);this.enqueueRaw(r);}track(e,t){this.enqueueEvent(e,"custom",t);}identify(e,t){this.identity.setUserId(e),this.enqueueEvent("$identify","auto",t),fetch(this.config.endpoint.replace(/\/+$/,"")+"/v1/identify",{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":this.config.writeKey},body:JSON.stringify({userId:e,anonymousId:this.identity.getAnonymousId(),traits:t||{}}),keepalive:true,credentials:"omit"}).catch(()=>{});}setUserProperties(e){let t=this.identity.getUserId();t?this.identify(t,e):this.enqueueEvent("$set_user_properties","auto",e);}page(e){this.enqueueEvent("$pageview","auto",e);}reset(){this.identity.reset(),this.queue.drain();}async flush(){if(this.flushing||this.queue.size()===0)return;this.flushing=true;let e=this.queue.drain();await I(e,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",e.length):(this.queue.requeue(e),this.debugLog("flush failed, requeued",e.length)),this.flushing=false;}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.identity.getSessionId()}startFlushTimer(){typeof setInterval>"u"||(this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval));}installPageHideFlush(){if(typeof document>"u")return;let e=()=>{let t=this.queue.drain();t.length!==0&&I(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint,useBeacon:true});};document.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&e();});}};var j=["input","textarea","select","[data-private]"],V=["[data-no-track]","script","style"];function T(i,e){for(let t of e)try{if(i.matches(t))return !0}catch{}return false}function l(i,e){let t=e.blockSelectors&&e.blockSelectors.length?e.blockSelectors:V;return T(i,t)}function Y(i,e){let t=e.maskTextSelectors&&e.maskTextSelectors.length?e.maskTextSelectors:j;return T(i,t)}function J(i,e){return Y(i,e)?"":(i.innerText||i.textContent||"").replace(/\s+/g," ").trim().slice(0,200)}function S(i){let e=[],t=i,n=0;for(;t&&t.nodeType===1&&n<6;){let r=t.tagName.toLowerCase();if(t.id){e.unshift(`${r}#${t.id}`);break}let o=(t.getAttribute("class")||"").split(/\s+/).filter(Boolean).slice(0,2).map(u=>"."+u).join(""),s="";if(t.parentElement){let u=Array.from(t.parentElement.children).filter(c=>c.tagName===t.tagName);u.length>1&&(s=`:nth-of-type(${u.indexOf(t)+1})`);}e.unshift(`${r}${o}${s}`),t=t.parentElement,n++;}return e.join(" > ")}function E(i,e){let t=i.tagName.toLowerCase(),n={selector:S(i),tag:t,text:J(i,e)},r=i.id;r&&(n.id=r);let o=i.getAttribute&&i.getAttribute("name");if(o&&(n.name=o),t==="a"){let u=i.href;u&&(n.href=u);}let s=i.getAttribute&&i.getAttribute("aria-label");return s&&(n.ariaLabel=s),n}function v(i){let e=i.tagName?.toLowerCase();if(!e)return false;if(e==="a"||e==="button")return true;let t=i.getAttribute&&i.getAttribute("role");return t==="button"||t==="link"||t==="tab"||t==="menuitem"}function _(i,e,t){typeof document>"u"||document.addEventListener("click",n=>{let r=n.target;if(r){if(e==="instrumented"){let o=r;for(;o&&o!==document.body&&!v(o);)o=o.parentElement;if(!o||o===document.body)return;r=o;}l(r,t)||i.enqueueEvent("$click","auto",E(r,t));}},true);}function k(i){if(typeof window>"u")return;let e=[],t=n=>{let r=Date.now();for(;e.length&&r-e[0].at>5e3;)e.shift();return e.some(o=>o.key===n)?false:(e.push({key:n,at:r}),true)};window.addEventListener("error",n=>{let r=n.message||String(n.error||"unknown"),o=`${r}|${n.filename}|${n.lineno}`;t(o)&&i.enqueueEvent("$error","auto",{message:r,source:n.filename,line:n.lineno,column:n.colno,stack:n.error&&n.error.stack||void 0});}),window.addEventListener("unhandledrejection",n=>{let r=n.reason,o=r instanceof Error?r.message:String(r),s=r instanceof Error?r.stack:void 0,u=`unhandled:${o}`;t(u)&&i.enqueueEvent("$error","auto",{kind:"unhandledrejection",message:o,stack:s});});}function A(i,e){typeof document>"u"||document.addEventListener("submit",t=>{let n=t.target;if(!n||n.tagName!=="FORM"||l(n,e))return;let r=n.querySelectorAll("input, select, textarea").length;i.enqueueEvent("$form_submit","auto",{selector:S(n),id:n.id||void 0,name:n.getAttribute("name")||void 0,action:n.getAttribute("action")||void 0,method:(n.getAttribute("method")||"get").toUpperCase(),fieldCount:r});},true);}var X=500;function x(i,e){if(typeof document>"u")return;let t=null,n=0;document.addEventListener("mouseover",r=>{let o=r.target;if(!o)return;let s=o;for(;s&&s!==document.body&&!v(s);)s=s.parentElement;if(!s||s===document.body||s===t||l(s,e))return;let u=Date.now();if(u-n<X){t=s;return}t=s,n=u,i.enqueueEvent("$hover","auto",E(s,e));},true);}function R(i){if(typeof window>"u"||typeof history>"u")return;let e=location.href,t=0,n=()=>{let o=Date.now();o-t<100||location.href===e&&t!==0||(e=location.href,t=o,i.markPageview(location.href),i.enqueueEvent("$pageview","auto",{url:location.href,path:location.pathname,title:document?.title,referrer:i.getPreviousUrl()}));};n();let r=o=>{let s=history[o];history[o]=function(...u){let c=s.apply(history,u);return n(),c};};r("pushState"),r("replaceState"),window.addEventListener("popstate",n),window.addEventListener("hashchange",n);}function U(i){if(typeof window>"u"||typeof document>"u")return;let e=0,t=0,n=location.href,r=()=>{location.href!==n&&(n=location.href,e=0,t=0);let o=Date.now();if(o-t<250)return;let s=document.documentElement,u=window.scrollY||s.scrollTop||0,c=window.innerHeight||s.clientHeight,C=s.scrollHeight-c;if(C<=0)return;let b=Math.max(0,Math.min(100,Math.round(u/C*100)));b-e<10||(e=b,t=o,i.enqueueEvent("$scroll","auto",{percent:b,pixels:u}));};window.addEventListener("scroll",r,{passive:true});}function L(i){if(typeof document>"u")return;let e=document.visibilityState;document.addEventListener("visibilitychange",()=>{let t=document.visibilityState;t!==e&&(e=t,i.enqueueEvent("$visibility","auto",{state:t}));});}function M(i){if(typeof PerformanceObserver>"u")return;let e=(t,n,r)=>{i.enqueueEvent("$web_vital","auto",{name:t,value:n,...r});};try{new PerformanceObserver(n=>{let r=n.getEntries(),o=r[r.length-1];o&&e("LCP",Math.round(o.startTime));}).observe({type:"largest-contentful-paint",buffered:!0});}catch{}try{let t=0;new PerformanceObserver(r=>{for(let o of r.getEntries())o.hadRecentInput||(t+=o.value);}).observe({type:"layout-shift",buffered:!0}),addEventListener("pagehide",()=>e("CLS",Math.round(t*1e3)/1e3));}catch{}try{new PerformanceObserver(n=>{let r=n.getEntries()[0];r&&e("FID",Math.round(r.processingStart-r.startTime));}).observe({type:"first-input",buffered:!0});}catch{}}var q={pageviews:true,clicks:"all",forms:true,scroll:true,visibility:true,mouseover:true,errors:true,vitals:true,sessions:true};function Q(i){return i===false?null:i===void 0||i===true?q:{...q,...i}}function $(i,e){let t=Q(e.autoCapture);if(!t)return;let n={maskTextSelectors:e.maskTextSelectors,blockSelectors:e.blockSelectors};if(t.pageviews&&R(i),t.clicks){let r=t.clicks==="instrumented"?"instrumented":"all";_(i,r,n);}t.forms&&A(i,n),t.scroll&&U(i),t.visibility&&L(i),t.mouseover&&x(i,n),t.errors&&k(i),t.vitals&&M(i);}function xe(i){let e=new y(i);return $(e,i),e}
2
- export{xe as createEcho};
1
+ function u(){let n=BigInt(Date.now()),e=crypto.getRandomValues(new Uint8Array(10)),t=new Uint8Array(16);t[0]=Number(n>>40n&0xffn),t[1]=Number(n>>32n&0xffn),t[2]=Number(n>>24n&0xffn),t[3]=Number(n>>16n&0xffn),t[4]=Number(n>>8n&0xffn),t[5]=Number(n&0xffn),t[6]=e[0]&15|112,t[7]=e[1],t[8]=e[2]&63|128,t[9]=e[3],t[10]=e[4],t[11]=e[5],t[12]=e[6],t[13]=e[7],t[14]=e[8],t[15]=e[9];let i=Array.from(t,r=>r.toString(16).padStart(2,"0")).join("");return `${i.slice(0,8)}-${i.slice(8,12)}-${i.slice(12,16)}-${i.slice(16,20)}-${i.slice(20)}`}var f="echo_anonymous_id",I="echo_user_id",d="echo_session_id",h="echo_last_activity";function X(){try{return typeof localStorage>"u"?null:(localStorage.setItem("__echo_probe__","1"),localStorage.removeItem("__echo_probe__"),localStorage)}catch{return null}}function W(){if(typeof location>"u")return null;let n=location.hostname;if(n==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(n))return null;let e=n.split(".");return e.length<2?null:"."+e.slice(-2).join(".")}function Q(n){if(typeof document>"u")return null;let e=document.cookie.match(new RegExp("(?:^|; )"+n+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}function z(n,e,t=365){if(typeof document>"u")return;let i=W(),r=new Date(Date.now()+t*864e5).toUTCString(),o=[`${n}=${encodeURIComponent(e)}`,`expires=${r}`,"path=/","SameSite=Lax"];i&&o.push(`domain=${i}`),typeof location<"u"&&location.protocol==="https:"&&o.push("Secure"),document.cookie=o.join("; ");}var m=class{constructor(e){this.storage=X();this.sessionTimeoutMs=e;let t=Q(f)||this.storage?.getItem(f)||null;t||(t=u()),this.anonymousId=t,this.persistAnon(),this.userId=this.storage?.getItem(I)||null;let i=Date.now(),r=this.storage?.getItem(d),o=parseInt(this.storage?.getItem(h)||"0",10);r&&i-o<e?this.sessionId=r:(this.sessionId=u(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=i,this.storage?.setItem(h,String(i));}persistAnon(){this.storage?.setItem(f,this.anonymousId),z(f,this.anonymousId);}touch(){let e=Date.now(),t=e-this.lastActivity>=this.sessionTimeoutMs,i;return t&&(i=this.sessionId,this.sessionId=u(),this.storage?.setItem(d,this.sessionId)),this.lastActivity=e,this.storage?.setItem(h,String(e)),{sessionStarted:t,oldSessionId:i}}setUserId(e){this.userId=e,this.storage?.setItem(I,e);}reset(){this.userId=null,this.storage?.removeItem(I),this.anonymousId=u(),this.persistAnon(),this.sessionId=u(),this.storage?.setItem(d,this.sessionId),this.lastActivity=Date.now(),this.storage?.setItem(h,String(this.lastActivity));}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getSessionId(){return this.sessionId}};var p="echo_queue_v1";var g=class{constructor(e){this.buffer=[];this.storage=e==="localstorage"&&typeof localStorage<"u"?localStorage:null,this.restore();}restore(){if(this.storage)try{let e=this.storage.getItem(p);e&&(this.buffer=JSON.parse(e));}catch{this.buffer=[];}}persist(){if(this.storage)try{this.storage.setItem(p,JSON.stringify(this.buffer));}catch{for(;this.buffer.length>50;){this.buffer.shift();try{this.storage.setItem(p,JSON.stringify(this.buffer));break}catch{}}}}enqueue(e){this.buffer.length>=1e3&&this.buffer.shift(),this.buffer.push(e),this.persist();}size(){return this.buffer.length}drain(){let e=this.buffer;return this.buffer=[],this.storage&&this.storage.removeItem(p),e}requeue(e){this.buffer=e.concat(this.buffer).slice(0,1e3),this.persist();}};async function C(n,e){if(n.length===0)return true;let t=e.endpoint.replace(/\/+$/,"")+"/v1/events",i=JSON.stringify({events:n});if(e.useBeacon&&typeof navigator<"u"&&navigator.sendBeacon){let r=`${t}?writeKey=${encodeURIComponent(e.writeKey)}`,o=new Blob([i],{type:"application/json"});return navigator.sendBeacon(r,o)}try{return (await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":e.writeKey},body:i,keepalive:!0,credentials:"omit"})).ok}catch{return false}}var y="echo_campaign_first",_="echo_campaign_last",G=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","utm_id"],Z=["gclid","gbraid","wbraid","dclid","fbclid","msclkid","ttclid","twclid","li_fat_id","igshid","yclid","epik","irclickid"];function U(n){let e={},t;try{t=new URL(n).search;}catch{return e}if(!t)return e;let i=new URLSearchParams(t);for(let r of G){let o=i.get(r);o&&(e[r]=o);}for(let r of Z){let o=i.get(r);o&&(e[r]=o);}return e}function k(n){try{let e=localStorage.getItem(n);return e?JSON.parse(e):null}catch{return null}}function A(n,e){try{localStorage.setItem(n,JSON.stringify(e));}catch{}}function T(){if(typeof location>"u"||typeof localStorage>"u")return;let n=U(location.href);if(Object.keys(n).length===0)return;let e={...n,_landedAt:new Date().toISOString(),_landingUrl:location.href,_referrer:typeof document<"u"?document.referrer:""};k(y)||A(y,e),A(_,e);}function L(){let n=typeof location<"u"?U(location.href):{},e=typeof localStorage<"u"?k(y):null,t=typeof localStorage<"u"?k(_):null,i={campaign:n};return e&&(i.campaign_first=e),t&&(i.campaign_last=t),i}function M(){try{localStorage.removeItem(y),localStorage.removeItem(_);}catch{}}var ee="https://events.moso.vn",te=50,ne=1e4,ie=1800*1e3,v=class{constructor(e){this.flushTimer=null;this.flushing=false;this.currentUrl="";this.previousUrl="";if(!e.writeKey)throw new Error("@mosovn/echo: writeKey is required");this.config={writeKey:e.writeKey,endpoint:e.endpoint??ee,flushAt:e.flushAt??te,flushInterval:e.flushInterval??ne,sessionTimeoutMs:e.sessionTimeoutMs??ie,debug:e.debug??false,...e},this.identity=new m(this.config.sessionTimeoutMs);let t=e.storage==="memory"?"memory":"localstorage";this.queue=new g(t),typeof location<"u"&&(this.currentUrl=location.href),typeof document<"u"&&(this.previousUrl=document.referrer||""),T(),this.startFlushTimer(),this.installPageHideFlush();}markPageview(e){e!==this.currentUrl&&(this.previousUrl=this.currentUrl,this.currentUrl=e,T());}getPreviousUrl(){return this.previousUrl}debugLog(...e){this.config.debug&&console.log("[echo]",...e);}buildEvent(e,t,i){let{sessionStarted:r,oldSessionId:o}=this.identity.touch();return r&&o&&(this.enqueueRaw({_id:u(),eventName:"$session_end",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:o}),this.enqueueRaw({_id:u(),eventName:"$session_start",eventType:"auto",anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId()})),{_id:u(),eventName:e,eventType:t,anonymousId:this.identity.getAnonymousId(),userId:this.identity.getUserId(),timestamp:new Date().toISOString(),sessionId:this.identity.getSessionId(),properties:i,context:this.collectContext()}}collectContext(){let e={lib:{name:"@mosovn/echo",version:"0.3.0"}};return typeof location<"u"&&(e.page={url:location.href,path:location.pathname,referrer:this.previousUrl,title:typeof document<"u"?document.title:""}),typeof navigator<"u"&&(e.locale=navigator.language),typeof screen<"u"&&(e.screen={width:screen.width,height:screen.height}),Object.assign(e,L()),e}enqueueRaw(e){let t=this.config.beforeSend,i=t?t(e):e;i&&(this.queue.enqueue(i),this.debugLog("enqueue",i.eventName,i._id),this.queue.size()>=this.config.flushAt&&this.flush());}enqueueEvent(e,t,i){let r=this.buildEvent(e,t,i);this.enqueueRaw(r);}track(e,t){this.enqueueEvent(e,"custom",t);}identify(e,t){this.identity.setUserId(e),this.enqueueEvent("$identify","auto",t),fetch(this.config.endpoint.replace(/\/+$/,"")+"/v1/identify",{method:"POST",headers:{"Content-Type":"application/json","X-Echo-Key":this.config.writeKey},body:JSON.stringify({userId:e,anonymousId:this.identity.getAnonymousId(),traits:t||{}}),keepalive:true,credentials:"omit"}).catch(()=>{});}setUserProperties(e){let t=this.identity.getUserId();t?this.identify(t,e):this.enqueueEvent("$set_user_properties","auto",e);}page(e){this.enqueueEvent("$pageview","auto",e);}reset(){this.identity.reset(),this.queue.drain(),M();}async flush(){if(this.flushing||this.queue.size()===0)return;this.flushing=true;let e=this.queue.drain();await C(e,{writeKey:this.config.writeKey,endpoint:this.config.endpoint})?this.debugLog("flush ok",e.length):(this.queue.requeue(e),this.debugLog("flush failed, requeued",e.length)),this.flushing=false;}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.identity.getSessionId()}startFlushTimer(){typeof setInterval>"u"||(this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval));}installPageHideFlush(){if(typeof document>"u")return;let e=()=>{let t=this.queue.drain();t.length!==0&&C(t,{writeKey:this.config.writeKey,endpoint:this.config.endpoint,useBeacon:true});};document.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&e();});}};var re=["input","textarea","select","[data-private]"],oe=["[data-no-track]","script","style"];function O(n,e){for(let t of e)try{if(n.matches(t))return !0}catch{}return false}function l(n,e){let t=e.blockSelectors&&e.blockSelectors.length?e.blockSelectors:oe;return O(n,t)}function se(n,e){let t=e.maskTextSelectors&&e.maskTextSelectors.length?e.maskTextSelectors:re;return O(n,t)}function ae(n,e){return se(n,e)?"":(n.innerText||n.textContent||"").replace(/\s+/g," ").trim().slice(0,200)}function R(n){let e=[],t=n,i=0;for(;t&&t.nodeType===1&&i<6;){let r=t.tagName.toLowerCase();if(t.id){e.unshift(`${r}#${t.id}`);break}let o=(t.getAttribute("class")||"").split(/\s+/).filter(Boolean).slice(0,2).map(a=>"."+a).join(""),s="";if(t.parentElement){let a=Array.from(t.parentElement.children).filter(c=>c.tagName===t.tagName);a.length>1&&(s=`:nth-of-type(${a.indexOf(t)+1})`);}e.unshift(`${r}${o}${s}`),t=t.parentElement,i++;}return e.join(" > ")}function b(n,e){let t=n.tagName.toLowerCase(),i={selector:R(n),tag:t,text:ae(n,e)},r=n.id;r&&(i.id=r);let o=n.getAttribute&&n.getAttribute("name");if(o&&(i.name=o),t==="a"){let a=n.href;a&&(i.href=a);}let s=n.getAttribute&&n.getAttribute("aria-label");return s&&(i.ariaLabel=s),i}function E(n){let e=n.tagName?.toLowerCase();if(!e)return false;if(e==="a"||e==="button")return true;let t=n.getAttribute&&n.getAttribute("role");return t==="button"||t==="link"||t==="tab"||t==="menuitem"}function q(n,e,t){typeof document>"u"||document.addEventListener("click",i=>{let r=i.target;if(r){if(e==="instrumented"){let o=r;for(;o&&o!==document.body&&!E(o);)o=o.parentElement;if(!o||o===document.body)return;r=o;}l(r,t)||n.enqueueEvent("$click","auto",b(r,t));}},true);}function $(n){if(typeof window>"u")return;let e=[],t=i=>{let r=Date.now();for(;e.length&&r-e[0].at>5e3;)e.shift();return e.some(o=>o.key===i)?false:(e.push({key:i,at:r}),true)};window.addEventListener("error",i=>{let r=i.message||String(i.error||"unknown"),o=`${r}|${i.filename}|${i.lineno}`;t(o)&&n.enqueueEvent("$error","auto",{message:r,source:i.filename,line:i.lineno,column:i.colno,stack:i.error&&i.error.stack||void 0});}),window.addEventListener("unhandledrejection",i=>{let r=i.reason,o=r instanceof Error?r.message:String(r),s=r instanceof Error?r.stack:void 0,a=`unhandled:${o}`;t(a)&&n.enqueueEvent("$error","auto",{kind:"unhandledrejection",message:o,stack:s});});}function D(n,e){typeof document>"u"||document.addEventListener("submit",t=>{let i=t.target;if(!i||i.tagName!=="FORM"||l(i,e))return;let r=i.querySelectorAll("input, select, textarea").length;n.enqueueEvent("$form_submit","auto",{selector:R(i),id:i.id||void 0,name:i.getAttribute("name")||void 0,action:i.getAttribute("action")||void 0,method:(i.getAttribute("method")||"get").toUpperCase(),fieldCount:r});},true);}var ue=500;function P(n,e){if(typeof document>"u")return;let t=null,i=0;document.addEventListener("mouseover",r=>{let o=r.target;if(!o)return;let s=o;for(;s&&s!==document.body&&!E(s);)s=s.parentElement;if(!s||s===document.body||s===t||l(s,e))return;let a=Date.now();if(a-i<ue){t=s;return}t=s,i=a,n.enqueueEvent("$hover","auto",b(s,e));},true);}function K(n){if(typeof window>"u"||typeof history>"u")return;let e=location.href,t=0,i=()=>{let o=Date.now();o-t<100||location.href===e&&t!==0||(e=location.href,t=o,n.markPageview(location.href),n.enqueueEvent("$pageview","auto",{url:location.href,path:location.pathname,title:document?.title,referrer:n.getPreviousUrl()}));};i();let r=o=>{let s=history[o];history[o]=function(...a){let c=s.apply(history,a);return i(),c};};r("pushState"),r("replaceState"),window.addEventListener("popstate",i),window.addEventListener("hashchange",i);}function N(n){if(typeof window>"u"||typeof document>"u")return;let e=0,t=0,i=location.href,r=()=>{location.href!==i&&(i=location.href,e=0,t=0);let o=Date.now();if(o-t<250)return;let s=document.documentElement,a=window.scrollY||s.scrollTop||0,c=window.innerHeight||s.clientHeight,x=s.scrollHeight-c;if(x<=0)return;let S=Math.max(0,Math.min(100,Math.round(a/x*100)));S-e<10||(e=S,t=o,n.enqueueEvent("$scroll","auto",{percent:S,pixels:a}));};window.addEventListener("scroll",r,{passive:true});}function F(n){if(typeof document>"u")return;let e=document.visibilityState;document.addEventListener("visibilitychange",()=>{let t=document.visibilityState;t!==e&&(e=t,n.enqueueEvent("$visibility","auto",{state:t}));});}function B(n){if(typeof PerformanceObserver>"u")return;let e=(t,i,r)=>{n.enqueueEvent("$web_vital","auto",{name:t,value:i,...r});};try{new PerformanceObserver(i=>{let r=i.getEntries(),o=r[r.length-1];o&&e("LCP",Math.round(o.startTime));}).observe({type:"largest-contentful-paint",buffered:!0});}catch{}try{let t=0;new PerformanceObserver(r=>{for(let o of r.getEntries())o.hadRecentInput||(t+=o.value);}).observe({type:"layout-shift",buffered:!0}),addEventListener("pagehide",()=>e("CLS",Math.round(t*1e3)/1e3));}catch{}try{new PerformanceObserver(i=>{let r=i.getEntries()[0];r&&e("FID",Math.round(r.processingStart-r.startTime));}).observe({type:"first-input",buffered:!0});}catch{}}var H={pageviews:true,clicks:"all",forms:true,scroll:true,visibility:true,mouseover:true,errors:true,vitals:true,sessions:true};function le(n){return n===false?null:n===void 0||n===true?H:{...H,...n}}function j(n,e){let t=le(e.autoCapture);if(!t)return;let i={maskTextSelectors:e.maskTextSelectors,blockSelectors:e.blockSelectors};if(t.pageviews&&K(n),t.clicks){let r=t.clicks==="instrumented"?"instrumented":"all";q(n,r,i);}t.forms&&D(n,i),t.scroll&&N(n),t.visibility&&F(n),t.mouseover&&P(n,i),t.errors&&$(n),t.vitals&&B(n);}function ce(n){let e=3735928559,t=1103547991;for(let r=0;r<n.length;r++){let o=n.charCodeAt(r);e=Math.imul(e^o,2654435761),t=Math.imul(t^o,1597334677);}return e=Math.imul(e^e>>>16,2246822507)^Math.imul(t^t>>>13,3266489909),t=Math.imul(t^t>>>16,2246822507)^Math.imul(e^e>>>13,3266489909),4294967296*(2097151&t)+(e>>>0)}function Y(n,e){return e<=0?false:e>=1?true:ce(n)%1e4/1e4<e}var w=class{constructor(e,t,i){this.client=e;this.intervalMs=t;this.seq=0;this.buffer=[];this.bytesPending=0;this.timer=null;this.endpoint=`${e.config.endpoint}${i}`,this.writeKey=e.config.writeKey,this.sessionId=e.identity.getSessionId(),this.timer=setInterval(()=>{this.flush();},t),typeof window<"u"&&(window.addEventListener("pagehide",()=>{this.flush(true);},{capture:true}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush(true);}));}push(e){this.buffer.push(e),this.bytesPending+=JSON.stringify(e).length,this.bytesPending>=5e5&&this.flush();}stop(){this.timer&&clearInterval(this.timer),this.timer=null;}async flush(e=false){if(this.buffer.length===0)return;let t=this.buffer.splice(0);this.bytesPending=0;let i=JSON.stringify({sessionId:this.sessionId,anonymousId:this.client.identity.getAnonymousId(),userId:this.client.identity.getUserId(),seq:this.seq++,events:t,ua:typeof navigator<"u"?navigator.userAgent:"",viewport:typeof window<"u"?{w:window.innerWidth,h:window.innerHeight}:void 0}),r=`${this.endpoint}?writeKey=${encodeURIComponent(this.writeKey)}`;if(e&&typeof navigator<"u"&&navigator.sendBeacon){try{navigator.sendBeacon(r,new Blob([i],{type:"application/json"}));}catch{}return}try{await fetch(r,{method:"POST",keepalive:!0,headers:{"X-Echo-Key":this.writeKey,"Content-Type":"application/json"},body:i});}catch(o){this.client.config.debug&&console.warn("[echo:replay] chunk upload failed",o);}}};var J={enabled:false,sampleRate:.1,maskTextSelectors:[],blockSelectors:[],blockUrlPatterns:[],chunkIntervalMs:3e4,chunkPath:"/v1/replays/chunk"};function fe(n){if(!n)return null;if(n===true)return {...J,enabled:true};let e={...J,...n,enabled:n.enabled??true};return e.enabled?e:null}function de(n){return typeof location>"u"?false:n.some(e=>typeof e=="string"?location.pathname.startsWith(e):e.test(location.pathname))}async function V(n){if(typeof window>"u"||typeof document>"u")return;let e=fe(n.config.replay);if(!e)return;if(de(e.blockUrlPatterns)){n.config.debug&&console.log("[echo:replay] blocked by URL pattern",location.pathname);return}let t=n.identity.getSessionId();if(!Y(t,e.sampleRate)){n.config.debug&&console.log("[echo:replay] session not sampled",t);return}let i;try{i=await import('rrweb');}catch(o){n.config.debug&&console.warn("[echo:replay] rrweb not available",o);return}let r=new w(n,e.chunkIntervalMs,e.chunkPath);i.record({emit:o=>r.push(o),maskTextSelector:e.maskTextSelectors.length?e.maskTextSelectors.join(","):void 0,blockSelector:e.blockSelectors.length?e.blockSelectors.join(","):void 0,maskInputOptions:{password:true,email:true,tel:true,date:true},recordCanvas:false,sampling:{mousemove:50,scroll:150,input:"last"}}),n.config.debug&&console.log("[echo:replay] recording session",t);}function et(n){let e=new v(n);return j(e,n),V(e),e}
2
+ export{et as createEcho};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mosovn/echo",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Echo SDK — event tracking for moso products. Works in browser and React Native (auto-picked by bundler).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,6 +33,9 @@
33
33
  "lint": "tsc --noEmit",
34
34
  "test": "echo \"no tests yet\""
35
35
  },
36
+ "dependencies": {
37
+ "rrweb": "^2.0.0-alpha.18"
38
+ },
36
39
  "peerDependencies": {
37
40
  "@react-native-async-storage/async-storage": ">=1.19.0",
38
41
  "react-native": ">=0.72.0",