ghostplay-sdk 0.1.8 → 0.1.9
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/index.global.js +4 -4
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var rrweb=require('rrweb');var A={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,trackNavigation:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function _(n){if(!n.projectId)throw new Error("[Ghostplay] projectId is required");if(!n.projectId.startsWith("gp_"))throw new Error('[Ghostplay] projectId must start with "gp_"');return {...A,...n}}function Y(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var f=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:Y(),projectId:this.projectId,startedAt:Date.now(),tags:[],metadata:{}},this.session}current(){if(!this.session)throw new Error("[Ghostplay] No active session");return this.session}identify(e,t){let r=this.current();r.userId=e,r.userTraits=t;}addTag(e){let t=this.current();t.tags.includes(e)||t.tags.push(e);}setMetadata(e){let t=this.current();t.metadata={...t.metadata,...e};}end(){this.session=null;}};var z=typeof window<"u"?window.MutationObserver:void 0,g=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){let e=typeof window<"u"?window.MutationObserver:void 0;z&&(window.MutationObserver=z),this.stopFn=rrweb.record({...this.rrwebConfig,emit:t=>{this.emit({type:"rrweb",timestamp:t.timestamp,sessionId:this.sessionId,data:t});}})??null,e&&(window.MutationObserver=e);}takeFullSnapshot(){this.stopFn&&rrweb.record.takeFullSnapshot();}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.onErrorHandler=null;this.onRejectionHandler=null;this.previousOnError=null;}start(){this.previousOnError=window.onerror,this.onErrorHandler=(e,t,r,s,i)=>{let a={message:String(e),stack:i?.stack,source:t??void 0,lineno:r??void 0,colno:s??void 0,type:"onerror"};return this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:a}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,i):false},this.onRejectionHandler=e=>{let t=e.reason,r={message:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,type:"unhandledrejection"};this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:r});},window.onerror=this.onErrorHandler,window.addEventListener("unhandledrejection",this.onRejectionHandler);}stop(){this.previousOnError?window.onerror=this.previousOnError:window.onerror=null,this.onRejectionHandler&&window.removeEventListener("unhandledrejection",this.onRejectionHandler),this.onErrorHandler=null,this.onRejectionHandler=null;}};var X=/ghostplay\.dev/;var y=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originalFetch=null;this.active=false;}start(){this.active=true,this.patchFetch();}stop(){this.active=false,this.restoreFetch();}patchFetch(){this.originalFetch=globalThis.fetch;let e=this;globalThis.fetch=async function(t,r){let s=typeof t=="string"?t:t instanceof URL?t.toString():t.url,i=r?.method??"GET";if(X.test(s))return e.originalFetch.call(globalThis,t,r);let a=Date.now(),O=V(r?.headers),L=Z(r?.body);try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let u={};p.headers.forEach((v,j)=>{u[j]=v;});let F;try{let v=p.headers.get("content-type")??"";if(v.includes("json")||v.includes("text")){let G=await p.clone().text();F=G.length>1048576?G.substring(0,1048576)+"...[truncated]":G;}}catch{}let W={method:i.toUpperCase(),url:s,status:p.status,statusText:p.statusText,requestHeaders:O,responseHeaders:u,requestBody:L,responseBody:F,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:W});}return p}catch(p){if(e.active){let u={method:i.toUpperCase(),url:s,requestHeaders:O,requestBody:L,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:u});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};function V(n){let e={};if(!n)return e;if(n instanceof Headers)n.forEach((t,r)=>{e[r]=t;});else if(Array.isArray(n))for(let[t,r]of n)e[t]=r;else for(let[t,r]of Object.entries(n))e[t]=r;return e}function Z(n){if(n){if(typeof n=="string")return n.length>1048576?n.substring(0,1048576)+"...[truncated]":n;if(n instanceof URLSearchParams)return n.toString()}}var q=["log","warn","error","info","debug"],E=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of q)this.originals[e]=console[e].bind(console),console[e]=(...t)=>{let r={level:e,args:t.map(s=>this.serialize(s))};this.emit({type:"console",timestamp:Date.now(),sessionId:this.sessionId,data:r}),this.originals[e]?.(...t);};}stop(){for(let e of q)this.originals[e]&&(console[e]=this.originals[e]);this.originals={};}serialize(e){if(e instanceof Error)return {__type:"Error",message:e.message,stack:e.stack};if(typeof e=="object"&&e!==null)try{return JSON.stringify(e),e}catch{return String(e)}return e}};var J={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},w=class{constructor(e,t){this.sessionId=e;this.emit=t;this.observers=[];}start(){typeof PerformanceObserver>"u"||(this.observeLCP(),this.observeFID(),this.observeCLS(),this.observeTTFB(),this.observeINP());}stop(){this.observers.forEach(e=>e.disconnect()),this.observers=[];}handleMetric(e,t){let[r,s]=J[e],i=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:i}});}observeLCP(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries(),s=r[r.length-1];s&&this.handleMetric("LCP",s.startTime);});e.observe({type:"largest-contentful-paint",buffered:!0}),this.observers.push(e);}catch{}}observeFID(){try{let e=new PerformanceObserver(t=>{let s=t.getEntries()[0];s&&this.handleMetric("FID",s.processingStart-s.startTime);});e.observe({type:"first-input",buffered:!0}),this.observers.push(e);}catch{}}observeCLS(){let e=0;try{let t=new PerformanceObserver(r=>{for(let s of r.getEntries())s.hadRecentInput||(e+=s.value);this.handleMetric("CLS",e);});t.observe({type:"layout-shift",buffered:!0}),this.observers.push(t);}catch{}}observeTTFB(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries()[0];r&&this.handleMetric("TTFB",r.responseStart);});e.observe({type:"navigation",buffered:!0}),this.observers.push(e);}catch{}}observeINP(){try{let e=new PerformanceObserver(t=>{for(let r of t.getEntries())this.handleMetric("INP",r.duration);});e.observe({type:"event",buffered:!0}),this.observers.push(e);}catch{}}};var b=class{constructor(e,t){this.sessionId=e;this.emit=t;this.clickHandler=null;this.recentClicks=[];}start(){this.clickHandler=e=>{let t=e.target;if(!t)return;let r=Date.now(),s=this.recentClicks.find(i=>i.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(i=>r-i<1e3),s.timestamps.length>=3){let i={selector:this.getSelector(t),clickCount:s.timestamps.length,x:e.clientX,y:e.clientY};this.emit({type:"rage-click",timestamp:r,sessionId:this.sessionId,data:i}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(i=>i.timestamps.length>0);},document.addEventListener("click",this.clickHandler,true);}stop(){this.clickHandler&&(document.removeEventListener("click",this.clickHandler,true),this.clickHandler=null),this.recentClicks=[];}getSelector(e){if(e.id)return `#${e.id}`;let t=e.getAttribute("data-testid");if(t)return `[data-testid="${t}"]`;let r=e.tagName.toLowerCase(),s=e.className?`.${e.className.split(" ").filter(Boolean).join(".")}`:"";return `${r}${s}`}};var k=class{constructor(e=50){this.maxSize=e;this.trail=[];}add(e,t,r){let s={category:e,message:t};r&&(s.data=r),this.trail.push(s),this.trail.length>this.maxSize&&(this.trail=this.trail.slice(-this.maxSize));}getTrail(){return [...this.trail]}clear(){this.trail=[];}};var I=class{constructor(e,t){this.sessionId=e;this.emit=t;this.currentUrl="";this.originalPushState=null;this.originalReplaceState=null;this.popstateHandler=null;this.hashchangeHandler=null;}start(){this.currentUrl=window.location.href,this.emitNavigation(null,this.currentUrl,"initial"),this.originalPushState=history.pushState.bind(history),history.pushState=(...e)=>{this.originalPushState(...e),this.handleUrlChange("pushState");},this.originalReplaceState=history.replaceState.bind(history),history.replaceState=(...e)=>{this.originalReplaceState(...e),this.handleUrlChange("replaceState");},this.popstateHandler=()=>this.handleUrlChange("popstate"),window.addEventListener("popstate",this.popstateHandler),this.hashchangeHandler=()=>this.handleUrlChange("hashchange"),window.addEventListener("hashchange",this.hashchangeHandler);}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.hashchangeHandler&&(window.removeEventListener("hashchange",this.hashchangeHandler),this.hashchangeHandler=null);}handleUrlChange(e){let t=window.location.href;if(t!==this.currentUrl){let r=this.currentUrl;this.currentUrl=t,this.emitNavigation(r,t,e);}}emitNavigation(e,t,r){let s={from:e,to:t,trigger:r};this.emit({type:"navigation",timestamp:Date.now(),sessionId:this.sessionId,data:s});}};var K=["set-cookie"],S=class{constructor(e){this.options=e;}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,K)),r&&(t.requestHeaders&&r.denyHeaders.length>0&&(t.requestHeaders=this.filterHeaders(t.requestHeaders,r.denyHeaders)),t.responseHeaders&&r.denyHeaders.length>0&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,r.denyHeaders)),r.denyBodyUrls.length>0&&this.urlMatchesDeny(e.url,r.denyBodyUrls)&&(t.requestBody&&(t.requestBody="[REDACTED]"),t.responseBody&&(t.responseBody="[REDACTED]"))),t}filterHeaders(e,t){let r=t.map(i=>i.toLowerCase()),s={};for(let[i,a]of Object.entries(e))r.includes(i.toLowerCase())||(s[i]=a);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var C=class{constructor(e){this.config=e;this.buffer=[];this.bufferBytes=0;this.flushTimer=null;}start(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushIntervalMs);}stop(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush();}send(e){let t=JSON.stringify(e).length;this.buffer.push(e),this.bufferBytes+=t,this.bufferBytes>=this.config.flushMaxBytes&&this.flush();}flush(){if(this.buffer.length===0)return;let e=this.buffer;this.buffer=[],this.bufferBytes=0;let t={projectId:this.config.projectId,sessionId:this.config.sessionId,events:e,sentAt:Date.now()},r=JSON.stringify(t),s=`${this.config.apiUrl}/events`,i=r.length<6e4;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:i}).then(a=>{a.ok||console.warn(`[Ghostplay] Event batch rejected (${a.status})`);}).catch(()=>{this.sendBeacon(s,r);});}catch{this.sendBeacon(s,r);}}sendBeacon(e,t){typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(e,new Blob([t],{type:"application/json"}));}};var Q="0.1.0",o=null,l=null,d=null,D=null,R=null,T=null,H=null,x=null,N=null,B=null,P=null,h=null;function M(){if(!l)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){N&&n.type!=="rrweb"&&N.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),h?.send(n);}function ee(n){$(),o=_(n),l=new f(o.projectId);let e=l.start();P=new S({networkSanitize:o.networkSanitize}),h=new C({apiUrl:o.apiUrl,projectId:o.projectId,sessionId:e.id,flushIntervalMs:o.flushIntervalMs,flushMaxBytes:o.flushMaxBytes}),h.start(),N=new k,d=new g(e.id,c,{maskAllInputs:o.maskAllInputs,...o.maskInputSelector?{maskInputSelector:o.maskInputSelector}:{},...o.blockSelector?{blockSelector:o.blockSelector}:{},...o.ignoreSelector?{ignoreSelector:o.ignoreSelector}:{},...o.rrwebConfig});let t=()=>{d?.start(),setTimeout(()=>d?.takeFullSnapshot(),3e3);};document.readyState==="complete"?requestAnimationFrame(()=>requestAnimationFrame(t)):window.addEventListener("load",()=>requestAnimationFrame(t),{once:true}),D=new m(e.id,c),D.start(),o.recordNetwork&&(R=new y(e.id,r=>{let s=P.sanitizeNetworkEvent(r.data);c({...r,data:s});}),R.start()),o.recordConsole&&(T=new E(e.id,c),T.start()),o.trackPerformance&&(H=new w(e.id,c),H.start()),o.trackRageClicks&&(x=new b(e.id,c),x.start()),o.trackNavigation&&(B=new I(e.id,c),B.start());}function te(n,e){M(),l.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:l.current().id,data:{userId:n,traits:e}});}function re(n){M(),l.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:l.current().id,data:{tag:n}});}function ne(n){M(),l.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:l.current().id,data:n});}function $(){d?.stop(),D?.stop(),R?.stop(),T?.stop(),H?.stop(),x?.stop(),B?.stop(),h?.stop(),l?.end(),d=null,D=null,R=null,T=null,H=null,x=null,B=null,h=null,l=null,N=null,P=null,o=null;}var se={VERSION:Q,init:ee,identify:te,tag:re,setMetadata:ne,stop:$},Be=se;
|
|
2
|
-
exports.Ghostplay=se;exports.VERSION=Q;exports.default=
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var rrweb=require('rrweb');var A={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,trackNavigation:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function _(n){if(!n.projectId)throw new Error("[Ghostplay] projectId is required");if(!n.projectId.startsWith("gp_"))throw new Error('[Ghostplay] projectId must start with "gp_"');return {...A,...n}}function W(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var f=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:W(),projectId:this.projectId,startedAt:Date.now(),tags:[],metadata:{}},this.session}current(){if(!this.session)throw new Error("[Ghostplay] No active session");return this.session}identify(e,t){let r=this.current();r.userId=e,r.userTraits=t;}addTag(e){let t=this.current();t.tags.includes(e)||t.tags.push(e);}setMetadata(e){let t=this.current();t.metadata={...t.metadata,...e};}end(){this.session=null;}};function Y(){if(!(typeof document>"u"))try{let n=document.createElement("iframe");return n.style.display="none",n.setAttribute("aria-hidden","true"),n.setAttribute("data-ghostplay","mo"),document.documentElement.appendChild(n),n.contentWindow?.MutationObserver}catch{return}}var g=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){let e=window.MutationObserver,t=Y();t&&(window.MutationObserver=t),this.stopFn=rrweb.record({...this.rrwebConfig,emit:r=>{this.emit({type:"rrweb",timestamp:r.timestamp,sessionId:this.sessionId,data:r});}})??null,window.MutationObserver=e;}takeFullSnapshot(){this.stopFn&&rrweb.record.takeFullSnapshot();}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.onErrorHandler=null;this.onRejectionHandler=null;this.previousOnError=null;}start(){this.previousOnError=window.onerror,this.onErrorHandler=(e,t,r,s,i)=>{let a={message:String(e),stack:i?.stack,source:t??void 0,lineno:r??void 0,colno:s??void 0,type:"onerror"};return this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:a}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,i):false},this.onRejectionHandler=e=>{let t=e.reason,r={message:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,type:"unhandledrejection"};this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:r});},window.onerror=this.onErrorHandler,window.addEventListener("unhandledrejection",this.onRejectionHandler);}stop(){this.previousOnError?window.onerror=this.previousOnError:window.onerror=null,this.onRejectionHandler&&window.removeEventListener("unhandledrejection",this.onRejectionHandler),this.onErrorHandler=null,this.onRejectionHandler=null;}};var X=/ghostplay\.dev/;var y=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originalFetch=null;this.active=false;}start(){this.active=true,this.patchFetch();}stop(){this.active=false,this.restoreFetch();}patchFetch(){this.originalFetch=globalThis.fetch;let e=this;globalThis.fetch=async function(t,r){let s=typeof t=="string"?t:t instanceof URL?t.toString():t.url,i=r?.method??"GET";if(X.test(s))return e.originalFetch.call(globalThis,t,r);let a=Date.now(),P=V(r?.headers),L=Z(r?.body);try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let h={};p.headers.forEach((v,j)=>{h[j]=v;});let F;try{let v=p.headers.get("content-type")??"";if(v.includes("json")||v.includes("text")){let G=await p.clone().text();F=G.length>1048576?G.substring(0,1048576)+"...[truncated]":G;}}catch{}let $={method:i.toUpperCase(),url:s,status:p.status,statusText:p.statusText,requestHeaders:P,responseHeaders:h,requestBody:L,responseBody:F,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:$});}return p}catch(p){if(e.active){let h={method:i.toUpperCase(),url:s,requestHeaders:P,requestBody:L,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:h});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};function V(n){let e={};if(!n)return e;if(n instanceof Headers)n.forEach((t,r)=>{e[r]=t;});else if(Array.isArray(n))for(let[t,r]of n)e[t]=r;else for(let[t,r]of Object.entries(n))e[t]=r;return e}function Z(n){if(n){if(typeof n=="string")return n.length>1048576?n.substring(0,1048576)+"...[truncated]":n;if(n instanceof URLSearchParams)return n.toString()}}var z=["log","warn","error","info","debug"],E=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of z)this.originals[e]=console[e].bind(console),console[e]=(...t)=>{let r={level:e,args:t.map(s=>this.serialize(s))};this.emit({type:"console",timestamp:Date.now(),sessionId:this.sessionId,data:r}),this.originals[e]?.(...t);};}stop(){for(let e of z)this.originals[e]&&(console[e]=this.originals[e]);this.originals={};}serialize(e){if(e instanceof Error)return {__type:"Error",message:e.message,stack:e.stack};if(typeof e=="object"&&e!==null)try{return JSON.stringify(e),e}catch{return String(e)}return e}};var J={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},w=class{constructor(e,t){this.sessionId=e;this.emit=t;this.observers=[];}start(){typeof PerformanceObserver>"u"||(this.observeLCP(),this.observeFID(),this.observeCLS(),this.observeTTFB(),this.observeINP());}stop(){this.observers.forEach(e=>e.disconnect()),this.observers=[];}handleMetric(e,t){let[r,s]=J[e],i=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:i}});}observeLCP(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries(),s=r[r.length-1];s&&this.handleMetric("LCP",s.startTime);});e.observe({type:"largest-contentful-paint",buffered:!0}),this.observers.push(e);}catch{}}observeFID(){try{let e=new PerformanceObserver(t=>{let s=t.getEntries()[0];s&&this.handleMetric("FID",s.processingStart-s.startTime);});e.observe({type:"first-input",buffered:!0}),this.observers.push(e);}catch{}}observeCLS(){let e=0;try{let t=new PerformanceObserver(r=>{for(let s of r.getEntries())s.hadRecentInput||(e+=s.value);this.handleMetric("CLS",e);});t.observe({type:"layout-shift",buffered:!0}),this.observers.push(t);}catch{}}observeTTFB(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries()[0];r&&this.handleMetric("TTFB",r.responseStart);});e.observe({type:"navigation",buffered:!0}),this.observers.push(e);}catch{}}observeINP(){try{let e=new PerformanceObserver(t=>{for(let r of t.getEntries())this.handleMetric("INP",r.duration);});e.observe({type:"event",buffered:!0}),this.observers.push(e);}catch{}}};var b=class{constructor(e,t){this.sessionId=e;this.emit=t;this.clickHandler=null;this.recentClicks=[];}start(){this.clickHandler=e=>{let t=e.target;if(!t)return;let r=Date.now(),s=this.recentClicks.find(i=>i.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(i=>r-i<1e3),s.timestamps.length>=3){let i={selector:this.getSelector(t),clickCount:s.timestamps.length,x:e.clientX,y:e.clientY};this.emit({type:"rage-click",timestamp:r,sessionId:this.sessionId,data:i}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(i=>i.timestamps.length>0);},document.addEventListener("click",this.clickHandler,true);}stop(){this.clickHandler&&(document.removeEventListener("click",this.clickHandler,true),this.clickHandler=null),this.recentClicks=[];}getSelector(e){if(e.id)return `#${e.id}`;let t=e.getAttribute("data-testid");if(t)return `[data-testid="${t}"]`;let r=e.tagName.toLowerCase(),s=e.className?`.${e.className.split(" ").filter(Boolean).join(".")}`:"";return `${r}${s}`}};var k=class{constructor(e=50){this.maxSize=e;this.trail=[];}add(e,t,r){let s={category:e,message:t};r&&(s.data=r),this.trail.push(s),this.trail.length>this.maxSize&&(this.trail=this.trail.slice(-this.maxSize));}getTrail(){return [...this.trail]}clear(){this.trail=[];}};var I=class{constructor(e,t){this.sessionId=e;this.emit=t;this.currentUrl="";this.originalPushState=null;this.originalReplaceState=null;this.popstateHandler=null;this.hashchangeHandler=null;}start(){this.currentUrl=window.location.href,this.emitNavigation(null,this.currentUrl,"initial"),this.originalPushState=history.pushState.bind(history),history.pushState=(...e)=>{this.originalPushState(...e),this.handleUrlChange("pushState");},this.originalReplaceState=history.replaceState.bind(history),history.replaceState=(...e)=>{this.originalReplaceState(...e),this.handleUrlChange("replaceState");},this.popstateHandler=()=>this.handleUrlChange("popstate"),window.addEventListener("popstate",this.popstateHandler),this.hashchangeHandler=()=>this.handleUrlChange("hashchange"),window.addEventListener("hashchange",this.hashchangeHandler);}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.hashchangeHandler&&(window.removeEventListener("hashchange",this.hashchangeHandler),this.hashchangeHandler=null);}handleUrlChange(e){let t=window.location.href;if(t!==this.currentUrl){let r=this.currentUrl;this.currentUrl=t,this.emitNavigation(r,t,e);}}emitNavigation(e,t,r){let s={from:e,to:t,trigger:r};this.emit({type:"navigation",timestamp:Date.now(),sessionId:this.sessionId,data:s});}};var K=["set-cookie"],S=class{constructor(e){this.options=e;}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,K)),r&&(t.requestHeaders&&r.denyHeaders.length>0&&(t.requestHeaders=this.filterHeaders(t.requestHeaders,r.denyHeaders)),t.responseHeaders&&r.denyHeaders.length>0&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,r.denyHeaders)),r.denyBodyUrls.length>0&&this.urlMatchesDeny(e.url,r.denyBodyUrls)&&(t.requestBody&&(t.requestBody="[REDACTED]"),t.responseBody&&(t.responseBody="[REDACTED]"))),t}filterHeaders(e,t){let r=t.map(i=>i.toLowerCase()),s={};for(let[i,a]of Object.entries(e))r.includes(i.toLowerCase())||(s[i]=a);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var C=class{constructor(e){this.config=e;this.buffer=[];this.bufferBytes=0;this.flushTimer=null;}start(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushIntervalMs);}stop(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush();}send(e){let t=JSON.stringify(e).length;this.buffer.push(e),this.bufferBytes+=t,this.bufferBytes>=this.config.flushMaxBytes&&this.flush();}flush(){if(this.buffer.length===0)return;let e=this.buffer;this.buffer=[],this.bufferBytes=0;let t={projectId:this.config.projectId,sessionId:this.config.sessionId,events:e,sentAt:Date.now()},r=JSON.stringify(t),s=`${this.config.apiUrl}/events`,i=r.length<6e4;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:i}).then(a=>{a.ok||console.warn(`[Ghostplay] Event batch rejected (${a.status})`);}).catch(()=>{this.sendBeacon(s,r);});}catch{this.sendBeacon(s,r);}}sendBeacon(e,t){typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(e,new Blob([t],{type:"application/json"}));}};var Q="0.1.0",o=null,l=null,d=null,D=null,R=null,T=null,H=null,x=null,B=null,N=null,M=null,u=null;function O(){if(!l)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){B&&n.type!=="rrweb"&&B.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),u?.send(n);}function ee(n){q(),o=_(n),l=new f(o.projectId);let e=l.start();M=new S({networkSanitize:o.networkSanitize}),u=new C({apiUrl:o.apiUrl,projectId:o.projectId,sessionId:e.id,flushIntervalMs:o.flushIntervalMs,flushMaxBytes:o.flushMaxBytes}),u.start(),B=new k,d=new g(e.id,c,{maskAllInputs:o.maskAllInputs,...o.maskInputSelector?{maskInputSelector:o.maskInputSelector}:{},...o.blockSelector?{blockSelector:o.blockSelector}:{},...o.ignoreSelector?{ignoreSelector:o.ignoreSelector}:{},...o.rrwebConfig});let t=()=>{d?.start(),setTimeout(()=>d?.takeFullSnapshot(),3e3);};document.readyState==="complete"?requestAnimationFrame(()=>requestAnimationFrame(t)):window.addEventListener("load",()=>requestAnimationFrame(t),{once:true}),D=new m(e.id,c),D.start(),o.recordNetwork&&(R=new y(e.id,r=>{let s=M.sanitizeNetworkEvent(r.data);c({...r,data:s});}),R.start()),o.recordConsole&&(T=new E(e.id,c),T.start()),o.trackPerformance&&(H=new w(e.id,c),H.start()),o.trackRageClicks&&(x=new b(e.id,c),x.start()),o.trackNavigation&&(N=new I(e.id,c),N.start());}function te(n,e){O(),l.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:l.current().id,data:{userId:n,traits:e}});}function re(n){O(),l.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:l.current().id,data:{tag:n}});}function ne(n){O(),l.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:l.current().id,data:n});}function q(){d?.stop(),D?.stop(),R?.stop(),T?.stop(),H?.stop(),x?.stop(),N?.stop(),u?.stop(),l?.end(),d=null,D=null,R=null,T=null,H=null,x=null,N=null,u=null,l=null,B=null,M=null,o=null;}var se={VERSION:Q,init:ee,identify:te,tag:re,setMetadata:ne,stop:q},Ne=se;
|
|
2
|
+
exports.Ghostplay=se;exports.VERSION=Q;exports.default=Ne;//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/config.ts","../src/session.ts","../src/recorder.ts","../src/modules/error-tracker.ts","../src/modules/network-interceptor.ts","../src/modules/console-interceptor.ts","../src/modules/performance-tracker.ts","../src/modules/rage-click-detector.ts","../src/modules/breadcrumbs.ts","../src/modules/navigation-tracker.ts","../src/privacy.ts","../src/transport.ts","../src/index.ts"],"names":["DEFAULT_CONFIG","resolveConfig","config","generateId","timestamp","random","SessionManager","projectId","userId","traits","session","tag","data","NativeMutationObserver","Recorder","sessionId","emit","rrwebConfig","patchedMO","record","event","ErrorTracker","message","source","lineno","colno","error","reason","GHOSTPLAY_URL_PATTERN","NetworkInterceptor","self","input","init","url","method","startTime","requestHeaders","extractHeaders","requestBody","extractBody","response","responseHeaders","value","key","responseBody","contentType","text","headers","result","body","LEVELS","ConsoleInterceptor","level","args","arg","THRESHOLDS","PerformanceTracker","o","metric","good","poor","rating","observer","list","entries","last","first","clsValue","entry","RageClickDetector","target","now","r","t","el","testId","className","Breadcrumbs","maxSize","category","crumb","NavigationTracker","trigger","newUrl","from","to","ALWAYS_DENY_RESPONSE_HEADERS","PrivacyFilter","options","denyList","denyLower","h","filtered","patterns","pattern","Transport","eventSize","events","batch","useKeepalive","res","VERSION","sessionManager","recorder","errorTracker","networkInterceptor","consoleInterceptor","performanceTracker","rageClickDetector","breadcrumbs","navigationTracker","privacyFilter","transport","ensureInitialized","handleEvent","userConfig","stop","startRecording","sanitized","identify","tagName","setMetadata","Ghostplay","index_default"],"mappings":"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","file":"index.js","sourcesContent":["// rrweb type aliases — rrweb 2.0.0-alpha.4 does not re-export these from its\n// package root, so we define compatible local aliases to keep the same shape.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype eventWithTime = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype recordOptions<T = eventWithTime> = Record<string, any>;\n\n// --- SDK Config ---\n\nexport interface NetworkSanitizeConfig {\n denyHeaders: string[];\n denyBodyUrls: string[];\n}\n\nexport interface GhostplayConfig {\n projectId: string;\n apiUrl?: string;\n\n // Privacy\n maskAllInputs?: boolean;\n maskInputSelector?: string;\n blockSelector?: string;\n ignoreSelector?: string;\n networkSanitize?: NetworkSanitizeConfig;\n\n // Feature toggles\n recordNetwork?: boolean;\n recordConsole?: boolean;\n trackPerformance?: boolean;\n trackRageClicks?: boolean;\n trackNavigation?: boolean;\n\n // Transport\n flushIntervalMs?: number;\n flushMaxBytes?: number;\n\n // rrweb passthrough\n rrwebConfig?: Partial<recordOptions<eventWithTime>>;\n}\n\nexport const DEFAULT_CONFIG: Required<\n Pick<\n GhostplayConfig,\n | 'apiUrl'\n | 'maskAllInputs'\n | 'recordNetwork'\n | 'recordConsole'\n | 'trackPerformance'\n | 'trackRageClicks'\n | 'trackNavigation'\n | 'flushIntervalMs'\n | 'flushMaxBytes'\n >\n> = {\n apiUrl: 'https://api.ghostplay.dev/v1',\n maskAllInputs: true,\n recordNetwork: true,\n recordConsole: true,\n trackPerformance: true,\n trackRageClicks: true,\n trackNavigation: true,\n flushIntervalMs: 5000,\n flushMaxBytes: 50_000,\n};\n\n// --- Session ---\n\nexport interface Session {\n id: string;\n projectId: string;\n startedAt: number;\n userId?: string;\n userTraits?: Record<string, unknown>;\n tags: string[];\n metadata: Record<string, unknown>;\n}\n\n// --- Events ---\n\nexport type GhostplayEventType =\n | 'rrweb'\n | 'error'\n | 'network'\n | 'console'\n | 'performance'\n | 'rage-click'\n | 'breadcrumb'\n | 'identify'\n | 'tag'\n | 'metadata'\n | 'navigation';\n\nexport interface GhostplayEvent {\n type: GhostplayEventType;\n timestamp: number;\n sessionId: string;\n data: unknown;\n}\n\nexport interface ErrorEventData {\n message: string;\n stack?: string;\n source?: string;\n lineno?: number;\n colno?: number;\n type: 'onerror' | 'unhandledrejection';\n}\n\nexport interface NetworkEventData {\n method: string;\n url: string;\n status?: number;\n statusText?: string;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n startTime: number;\n endTime?: number;\n duration?: number;\n}\n\nexport interface ConsoleEventData {\n level: 'log' | 'warn' | 'error' | 'info' | 'debug';\n args: unknown[];\n}\n\nexport interface PerformanceEventData {\n metric: 'LCP' | 'FID' | 'CLS' | 'TTFB' | 'INP';\n value: number;\n rating: 'good' | 'needs-improvement' | 'poor';\n}\n\nexport interface RageClickEventData {\n selector: string;\n clickCount: number;\n x: number;\n y: number;\n}\n\nexport interface BreadcrumbEventData {\n category: 'navigation' | 'click' | 'input' | 'error' | 'network' | 'custom';\n message: string;\n data?: Record<string, unknown>;\n}\n\nexport interface NavigationEventData {\n from: string | null;\n to: string;\n trigger: 'pushState' | 'replaceState' | 'popstate' | 'hashchange' | 'initial';\n}\n\n// --- Transport ---\n\nexport interface EventBatch {\n projectId: string;\n sessionId: string;\n events: GhostplayEvent[];\n sentAt: number;\n}\n","import { type GhostplayConfig, DEFAULT_CONFIG } from './types';\n\nexport type ResolvedConfig = GhostplayConfig & typeof DEFAULT_CONFIG;\n\nexport function resolveConfig(config: GhostplayConfig): ResolvedConfig {\n if (!config.projectId) {\n throw new Error('[Ghostplay] projectId is required');\n }\n if (!config.projectId.startsWith('gp_')) {\n throw new Error('[Ghostplay] projectId must start with \"gp_\"');\n }\n\n return { ...DEFAULT_CONFIG, ...config };\n}\n","import type { Session } from './types';\n\nfunction generateId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 10);\n return `gs_${timestamp}_${random}`;\n}\n\nexport class SessionManager {\n private session: Session | null = null;\n\n constructor(private projectId: string) {}\n\n start(): Session {\n this.session = {\n id: generateId(),\n projectId: this.projectId,\n startedAt: Date.now(),\n tags: [],\n metadata: {},\n };\n return this.session;\n }\n\n current(): Session {\n if (!this.session) {\n throw new Error('[Ghostplay] No active session');\n }\n return this.session;\n }\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n const session = this.current();\n session.userId = userId;\n session.userTraits = traits;\n }\n\n addTag(tag: string): void {\n const session = this.current();\n if (!session.tags.includes(tag)) {\n session.tags.push(tag);\n }\n }\n\n setMetadata(data: Record<string, unknown>): void {\n const session = this.current();\n session.metadata = { ...session.metadata, ...data };\n }\n\n end(): void {\n this.session = null;\n }\n}\n","import { record } from 'rrweb';\nimport type { GhostplayEvent } from './types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\n// Capture the native MutationObserver at module-load time, before any\n// third-party script (LogRocket, FullStory, Datadog RUM, etc.) can\n// monkey-patch it. rrweb relies on MutationObserver to track DOM\n// changes — a patched version may silently swallow mutations.\nconst NativeMutationObserver: typeof MutationObserver | undefined =\n typeof window !== 'undefined' ? window.MutationObserver : undefined;\n\nexport class Recorder {\n private stopFn: (() => void) | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback,\n private rrwebConfig?: Record<string, any>\n ) {}\n\n start(): void {\n // Temporarily restore the native MutationObserver so rrweb creates\n // its observer from the unpatched constructor.\n const patchedMO = typeof window !== 'undefined' ? window.MutationObserver : undefined;\n if (NativeMutationObserver) {\n window.MutationObserver = NativeMutationObserver;\n }\n\n this.stopFn = record({\n ...this.rrwebConfig,\n emit: (event: any) => {\n this.emit({\n type: 'rrweb',\n timestamp: event.timestamp,\n sessionId: this.sessionId,\n data: event,\n });\n },\n }) ?? null;\n\n // Restore the (potentially patched) MutationObserver so other tools\n // continue to work.\n if (patchedMO) {\n window.MutationObserver = patchedMO;\n }\n }\n\n /** Force a new FullSnapshot (useful as a one-time safety net for\n * async content that wasn't in the initial snapshot). */\n takeFullSnapshot(): void {\n if (this.stopFn) {\n record.takeFullSnapshot();\n }\n }\n\n stop(): void {\n if (this.stopFn) {\n this.stopFn();\n this.stopFn = null;\n }\n }\n}\n","import type { GhostplayEvent, ErrorEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nexport class ErrorTracker {\n private onErrorHandler: OnErrorEventHandler | null = null;\n private onRejectionHandler: ((e: PromiseRejectionEvent) => void) | null = null;\n private previousOnError: OnErrorEventHandler | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.previousOnError = window.onerror;\n\n this.onErrorHandler = (message, source, lineno, colno, error) => {\n const data: ErrorEventData = {\n message: String(message),\n stack: error?.stack,\n source: source ?? undefined,\n lineno: lineno ?? undefined,\n colno: colno ?? undefined,\n type: 'onerror',\n };\n\n this.emit({\n type: 'error',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n\n if (this.previousOnError) {\n return this.previousOnError.call(window, message, source, lineno, colno, error);\n }\n return false;\n };\n\n this.onRejectionHandler = (e: PromiseRejectionEvent) => {\n const reason = e.reason;\n const data: ErrorEventData = {\n message:\n reason instanceof Error ? reason.message : String(reason),\n stack: reason instanceof Error ? reason.stack : undefined,\n type: 'unhandledrejection',\n };\n\n this.emit({\n type: 'error',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n };\n\n window.onerror = this.onErrorHandler;\n window.addEventListener('unhandledrejection', this.onRejectionHandler);\n }\n\n stop(): void {\n if (this.previousOnError) {\n window.onerror = this.previousOnError;\n } else {\n window.onerror = null;\n }\n\n if (this.onRejectionHandler) {\n window.removeEventListener('unhandledrejection', this.onRejectionHandler);\n }\n\n this.onErrorHandler = null;\n this.onRejectionHandler = null;\n }\n}\n","import type { GhostplayEvent, NetworkEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nconst GHOSTPLAY_URL_PATTERN = /ghostplay\\.dev/;\nconst MAX_BODY_SIZE = 1024 * 1024; // 1MB limit per body\n\nexport class NetworkInterceptor {\n private originalFetch: typeof fetch | null = null;\n private active = false;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.active = true;\n this.patchFetch();\n }\n\n stop(): void {\n this.active = false;\n this.restoreFetch();\n }\n\n private patchFetch(): void {\n this.originalFetch = globalThis.fetch;\n const self = this;\n\n globalThis.fetch = async function (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n const method = init?.method ?? 'GET';\n\n if (GHOSTPLAY_URL_PATTERN.test(url)) {\n return self.originalFetch!.call(globalThis, input, init);\n }\n\n const startTime = Date.now();\n\n // Extract request headers\n const requestHeaders = extractHeaders(init?.headers);\n\n // Extract request body\n const requestBody = extractBody(init?.body);\n\n try {\n const response = await self.originalFetch!.call(\n globalThis,\n input,\n init\n );\n\n if (self.active) {\n // Extract response headers\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n // Clone response to read body without consuming the original\n let responseBody: string | undefined;\n try {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('json') || contentType.includes('text')) {\n const clone = response.clone();\n const text = await clone.text();\n responseBody = text.length > MAX_BODY_SIZE\n ? text.substring(0, MAX_BODY_SIZE) + '...[truncated]'\n : text;\n }\n } catch {\n // Body read failed — skip\n }\n\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n status: response.status,\n statusText: response.statusText,\n requestHeaders,\n responseHeaders,\n requestBody,\n responseBody,\n startTime,\n endTime: Date.now(),\n duration: Date.now() - startTime,\n };\n\n self.emit({\n type: 'network',\n timestamp: startTime,\n sessionId: self.sessionId,\n data,\n });\n }\n\n return response;\n } catch (error) {\n if (self.active) {\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n requestHeaders,\n requestBody,\n startTime,\n endTime: Date.now(),\n duration: Date.now() - startTime,\n };\n\n self.emit({\n type: 'network',\n timestamp: startTime,\n sessionId: self.sessionId,\n data,\n });\n }\n\n throw error;\n }\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch) {\n globalThis.fetch = this.originalFetch;\n this.originalFetch = null;\n }\n }\n}\n\nfunction extractHeaders(headers: HeadersInit | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => { result[key] = value; });\n } else if (Array.isArray(headers)) {\n for (const [key, value] of headers) { result[key] = value; }\n } else {\n for (const [key, value] of Object.entries(headers)) { result[key] = value; }\n }\n\n return result;\n}\n\nfunction extractBody(body: BodyInit | null | undefined): string | undefined {\n if (!body) return undefined;\n\n if (typeof body === 'string') {\n return body.length > MAX_BODY_SIZE\n ? body.substring(0, MAX_BODY_SIZE) + '...[truncated]'\n : body;\n }\n\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n\n // FormData, Blob, ArrayBuffer, ReadableStream — skip (too complex/binary)\n return undefined;\n}\n","import type { GhostplayEvent, ConsoleEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\ntype ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug'];\n\nexport class ConsoleInterceptor {\n private originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {};\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n for (const level of LEVELS) {\n this.originals[level] = console[level].bind(console);\n console[level] = (...args: unknown[]) => {\n const data: ConsoleEventData = {\n level,\n args: args.map((arg) => this.serialize(arg)),\n };\n this.emit({\n type: 'console',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n this.originals[level]?.(...args);\n };\n }\n }\n\n stop(): void {\n for (const level of LEVELS) {\n if (this.originals[level]) {\n console[level] = this.originals[level] as any;\n }\n }\n this.originals = {};\n }\n\n private serialize(arg: unknown): unknown {\n if (arg instanceof Error) {\n return { __type: 'Error', message: arg.message, stack: arg.stack };\n }\n if (typeof arg === 'object' && arg !== null) {\n try { JSON.stringify(arg); return arg; } catch { return String(arg); }\n }\n return arg;\n }\n}\n","import type { GhostplayEvent, PerformanceEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\ntype Metric = PerformanceEventData['metric'];\n\nconst THRESHOLDS: Record<Metric, [number, number]> = {\n LCP: [1000, 4000],\n FID: [100, 300],\n CLS: [0.1, 0.25],\n TTFB: [800, 1800],\n INP: [200, 500],\n};\n\nexport class PerformanceTracker {\n private observers: PerformanceObserver[] = [];\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n if (typeof PerformanceObserver === 'undefined') return;\n this.observeLCP();\n this.observeFID();\n this.observeCLS();\n this.observeTTFB();\n this.observeINP();\n }\n\n stop(): void {\n this.observers.forEach((o) => o.disconnect());\n this.observers = [];\n }\n\n handleMetric(metric: Metric, value: number): void {\n const [good, poor] = THRESHOLDS[metric];\n const rating: PerformanceEventData['rating'] =\n value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n this.emit({\n type: 'performance',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data: { metric, value, rating },\n });\n }\n\n private observeLCP(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n const last = entries[entries.length - 1];\n if (last) this.handleMetric('LCP', last.startTime);\n });\n observer.observe({ type: 'largest-contentful-paint', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeFID(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n const first = entries[0] as any;\n if (first) this.handleMetric('FID', first.processingStart - first.startTime);\n });\n observer.observe({ type: 'first-input', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeCLS(): void {\n let clsValue = 0;\n try {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as any[]) {\n if (!entry.hadRecentInput) clsValue += entry.value;\n }\n this.handleMetric('CLS', clsValue);\n });\n observer.observe({ type: 'layout-shift', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeTTFB(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as any;\n if (entry) this.handleMetric('TTFB', entry.responseStart);\n });\n observer.observe({ type: 'navigation', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeINP(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as any[]) {\n this.handleMetric('INP', entry.duration);\n }\n });\n observer.observe({ type: 'event', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n}\n","import type { GhostplayEvent, RageClickEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nconst RAGE_THRESHOLD = 3;\nconst RAGE_WINDOW_MS = 1000;\n\ninterface ClickRecord {\n element: Element;\n timestamps: number[];\n}\n\nexport class RageClickDetector {\n private clickHandler: ((e: MouseEvent) => void) | null = null;\n private recentClicks: ClickRecord[] = [];\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.clickHandler = (e: MouseEvent) => {\n const target = e.target as Element;\n if (!target) return;\n const now = Date.now();\n let record = this.recentClicks.find((r) => r.element === target);\n if (!record) {\n record = { element: target, timestamps: [] };\n this.recentClicks.push(record);\n }\n record.timestamps.push(now);\n record.timestamps = record.timestamps.filter((t) => now - t < RAGE_WINDOW_MS);\n if (record.timestamps.length >= RAGE_THRESHOLD) {\n const data: RageClickEventData = {\n selector: this.getSelector(target),\n clickCount: record.timestamps.length,\n x: e.clientX,\n y: e.clientY,\n };\n this.emit({\n type: 'rage-click',\n timestamp: now,\n sessionId: this.sessionId,\n data,\n });\n record.timestamps = [];\n }\n this.recentClicks = this.recentClicks.filter((r) => r.timestamps.length > 0);\n };\n document.addEventListener('click', this.clickHandler, true);\n }\n\n stop(): void {\n if (this.clickHandler) {\n document.removeEventListener('click', this.clickHandler, true);\n this.clickHandler = null;\n }\n this.recentClicks = [];\n }\n\n private getSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const testId = el.getAttribute('data-testid');\n if (testId) return `[data-testid=\"${testId}\"]`;\n const tag = el.tagName.toLowerCase();\n const className = el.className\n ? `.${el.className.split(' ').filter(Boolean).join('.')}`\n : '';\n return `${tag}${className}`;\n }\n}\n","import type { BreadcrumbEventData } from '../types';\n\nexport class Breadcrumbs {\n private trail: BreadcrumbEventData[] = [];\n\n constructor(private maxSize: number = 50) {}\n\n add(\n category: BreadcrumbEventData['category'],\n message: string,\n data?: Record<string, unknown>\n ): void {\n const crumb: BreadcrumbEventData = { category, message };\n if (data) crumb.data = data;\n this.trail.push(crumb);\n if (this.trail.length > this.maxSize) {\n this.trail = this.trail.slice(-this.maxSize);\n }\n }\n\n getTrail(): BreadcrumbEventData[] {\n return [...this.trail];\n }\n\n clear(): void {\n this.trail = [];\n }\n}\n","import type { GhostplayEvent, NavigationEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nexport class NavigationTracker {\n private currentUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n private popstateHandler: (() => void) | null = null;\n private hashchangeHandler: (() => void) | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.currentUrl = window.location.href;\n\n // Emit initial navigation event\n this.emitNavigation(null, this.currentUrl, 'initial');\n\n // Patch history.pushState\n this.originalPushState = history.pushState.bind(history);\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this.handleUrlChange('pushState');\n };\n\n // Patch history.replaceState\n this.originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this.handleUrlChange('replaceState');\n };\n\n // Listen for popstate (back/forward)\n this.popstateHandler = () => this.handleUrlChange('popstate');\n window.addEventListener('popstate', this.popstateHandler);\n\n // Listen for hashchange\n this.hashchangeHandler = () => this.handleUrlChange('hashchange');\n window.addEventListener('hashchange', this.hashchangeHandler);\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n this.originalPushState = null;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n this.originalReplaceState = null;\n }\n if (this.popstateHandler) {\n window.removeEventListener('popstate', this.popstateHandler);\n this.popstateHandler = null;\n }\n if (this.hashchangeHandler) {\n window.removeEventListener('hashchange', this.hashchangeHandler);\n this.hashchangeHandler = null;\n }\n }\n\n private handleUrlChange(trigger: NavigationEventData['trigger']): void {\n const newUrl = window.location.href;\n if (newUrl !== this.currentUrl) {\n const from = this.currentUrl;\n this.currentUrl = newUrl;\n this.emitNavigation(from, newUrl, trigger);\n }\n }\n\n private emitNavigation(from: string | null, to: string, trigger: NavigationEventData['trigger']): void {\n const data: NavigationEventData = { from, to, trigger };\n this.emit({\n type: 'navigation',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n }\n}\n","import type { GhostplayConfig, NetworkEventData } from './types';\n\nconst ALWAYS_DENY_RESPONSE_HEADERS = ['set-cookie'];\n\ninterface PrivacyOptions {\n networkSanitize?: GhostplayConfig['networkSanitize'];\n}\n\nexport class PrivacyFilter {\n constructor(private options: PrivacyOptions) {}\n\n sanitizeNetworkEvent(event: NetworkEventData): NetworkEventData {\n const result = { ...event };\n const config = this.options.networkSanitize;\n\n // Always remove sensitive response headers\n if (result.responseHeaders) {\n result.responseHeaders = this.filterHeaders(\n result.responseHeaders,\n ALWAYS_DENY_RESPONSE_HEADERS\n );\n }\n\n if (!config) return result;\n\n // Remove denied request headers\n if (result.requestHeaders && config.denyHeaders.length > 0) {\n result.requestHeaders = this.filterHeaders(\n result.requestHeaders,\n config.denyHeaders\n );\n }\n\n // Remove denied response headers\n if (result.responseHeaders && config.denyHeaders.length > 0) {\n result.responseHeaders = this.filterHeaders(\n result.responseHeaders,\n config.denyHeaders\n );\n }\n\n // Redact body for denied URLs\n if (config.denyBodyUrls.length > 0 && this.urlMatchesDeny(event.url, config.denyBodyUrls)) {\n if (result.requestBody) result.requestBody = '[REDACTED]';\n if (result.responseBody) result.responseBody = '[REDACTED]';\n }\n\n return result;\n }\n\n private filterHeaders(\n headers: Record<string, string>,\n denyList: string[]\n ): Record<string, string> {\n const denyLower = denyList.map((h) => h.toLowerCase());\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (!denyLower.includes(key.toLowerCase())) {\n filtered[key] = value;\n }\n }\n return filtered;\n }\n\n private urlMatchesDeny(url: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n const regex = new RegExp(\n '^' + pattern.replace(/\\*/g, '.*') + '$'\n );\n return regex.test(url);\n });\n }\n}\n","import type { GhostplayEvent, EventBatch } from './types';\n\ninterface TransportConfig {\n apiUrl: string;\n projectId: string;\n sessionId: string;\n flushIntervalMs: number;\n flushMaxBytes: number;\n}\n\nconst KEEPALIVE_LIMIT = 60_000; // Stay under 64KB browser limit\n\nexport class Transport {\n private buffer: GhostplayEvent[] = [];\n private bufferBytes = 0;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(private config: TransportConfig) {}\n\n start(): void {\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushIntervalMs);\n }\n\n stop(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n this.flush();\n }\n\n send(event: GhostplayEvent): void {\n const eventSize = JSON.stringify(event).length;\n this.buffer.push(event);\n this.bufferBytes += eventSize;\n if (this.bufferBytes >= this.config.flushMaxBytes) {\n this.flush();\n }\n }\n\n private flush(): void {\n if (this.buffer.length === 0) return;\n const events = this.buffer;\n this.buffer = [];\n this.bufferBytes = 0;\n const batch: EventBatch = {\n projectId: this.config.projectId,\n sessionId: this.config.sessionId,\n events,\n sentAt: Date.now(),\n };\n const body = JSON.stringify(batch);\n const url = `${this.config.apiUrl}/events`;\n\n // Use keepalive only for small payloads (Safari enforces 64KB limit strictly)\n const useKeepalive = body.length < KEEPALIVE_LIMIT;\n\n try {\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-project-id': this.config.projectId,\n },\n body,\n keepalive: useKeepalive,\n })\n .then((res) => {\n if (!res.ok) {\n console.warn(`[Ghostplay] Event batch rejected (${res.status})`);\n }\n })\n .catch(() => {\n // Fallback to sendBeacon\n this.sendBeacon(url, body);\n });\n } catch {\n this.sendBeacon(url, body);\n }\n }\n\n private sendBeacon(url: string, body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }));\n }\n }\n}\n","import { resolveConfig, type ResolvedConfig } from './config';\nimport { SessionManager } from './session';\nimport { Recorder } from './recorder';\nimport { ErrorTracker } from './modules/error-tracker';\nimport { NetworkInterceptor } from './modules/network-interceptor';\nimport { ConsoleInterceptor } from './modules/console-interceptor';\nimport { PerformanceTracker } from './modules/performance-tracker';\nimport { RageClickDetector } from './modules/rage-click-detector';\nimport { Breadcrumbs } from './modules/breadcrumbs';\nimport { NavigationTracker } from './modules/navigation-tracker';\nimport { PrivacyFilter } from './privacy';\nimport { Transport } from './transport';\nimport type { GhostplayConfig, GhostplayEvent } from './types';\n\nexport const VERSION = '0.1.0';\n\nlet config: ResolvedConfig | null = null;\nlet sessionManager: SessionManager | null = null;\nlet recorder: Recorder | null = null;\nlet errorTracker: ErrorTracker | null = null;\nlet networkInterceptor: NetworkInterceptor | null = null;\nlet consoleInterceptor: ConsoleInterceptor | null = null;\nlet performanceTracker: PerformanceTracker | null = null;\nlet rageClickDetector: RageClickDetector | null = null;\nlet breadcrumbs: Breadcrumbs | null = null;\nlet navigationTracker: NavigationTracker | null = null;\nlet privacyFilter: PrivacyFilter | null = null;\nlet transport: Transport | null = null;\n\nfunction ensureInitialized(): void {\n if (!sessionManager) {\n throw new Error('[Ghostplay] SDK not initialized. Call Ghostplay.init() first.');\n }\n}\n\nfunction handleEvent(event: GhostplayEvent): void {\n if (breadcrumbs && event.type !== 'rrweb') {\n breadcrumbs.add(\n event.type === 'error'\n ? 'error'\n : event.type === 'network'\n ? 'network'\n : event.type === 'rage-click'\n ? 'click'\n : 'custom',\n `${event.type}: ${JSON.stringify(event.data).substring(0, 100)}`\n );\n }\n transport?.send(event);\n}\n\nfunction init(userConfig: GhostplayConfig): void {\n stop();\n config = resolveConfig(userConfig);\n sessionManager = new SessionManager(config.projectId);\n const session = sessionManager.start();\n\n privacyFilter = new PrivacyFilter({\n networkSanitize: config.networkSanitize,\n });\n\n transport = new Transport({\n apiUrl: config.apiUrl,\n projectId: config.projectId,\n sessionId: session.id,\n flushIntervalMs: config.flushIntervalMs,\n flushMaxBytes: config.flushMaxBytes,\n });\n transport.start();\n\n breadcrumbs = new Breadcrumbs();\n\n recorder = new Recorder(session.id, handleEvent, {\n maskAllInputs: config.maskAllInputs,\n ...(config.maskInputSelector ? { maskInputSelector: config.maskInputSelector } : {}),\n ...(config.blockSelector ? { blockSelector: config.blockSelector } : {}),\n ...(config.ignoreSelector ? { ignoreSelector: config.ignoreSelector } : {}),\n ...config.rrwebConfig,\n });\n\n // Defer recording start so the initial FullSnapshot captures the\n // rendered DOM shell rather than an empty SPA root element.\n const startRecording = () => {\n recorder?.start();\n // One-time follow-up snapshot to capture async-loaded content\n // (data fetched via API after initial render). Runs once, not\n // periodically — avoids the storage overhead of checkoutEveryNms.\n setTimeout(() => recorder?.takeFullSnapshot(), 3000);\n };\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => requestAnimationFrame(startRecording));\n } else {\n window.addEventListener('load', () =>\n requestAnimationFrame(startRecording), { once: true });\n }\n\n errorTracker = new ErrorTracker(session.id, handleEvent);\n errorTracker.start();\n\n if (config.recordNetwork) {\n networkInterceptor = new NetworkInterceptor(session.id, (event) => {\n const sanitized = privacyFilter!.sanitizeNetworkEvent(event.data as any);\n handleEvent({ ...event, data: sanitized });\n });\n networkInterceptor.start();\n }\n\n if (config.recordConsole) {\n consoleInterceptor = new ConsoleInterceptor(session.id, handleEvent);\n consoleInterceptor.start();\n }\n\n if (config.trackPerformance) {\n performanceTracker = new PerformanceTracker(session.id, handleEvent);\n performanceTracker.start();\n }\n\n if (config.trackRageClicks) {\n rageClickDetector = new RageClickDetector(session.id, handleEvent);\n rageClickDetector.start();\n }\n\n if (config.trackNavigation) {\n navigationTracker = new NavigationTracker(session.id, handleEvent);\n navigationTracker.start();\n }\n}\n\nfunction identify(userId: string, traits?: Record<string, unknown>): void {\n ensureInitialized();\n sessionManager!.identify(userId, traits);\n handleEvent({\n type: 'identify',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data: { userId, traits },\n });\n}\n\nfunction tag(tagName: string): void {\n ensureInitialized();\n sessionManager!.addTag(tagName);\n handleEvent({\n type: 'tag',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data: { tag: tagName },\n });\n}\n\nfunction setMetadata(data: Record<string, unknown>): void {\n ensureInitialized();\n sessionManager!.setMetadata(data);\n handleEvent({\n type: 'metadata',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data,\n });\n}\n\nfunction stop(): void {\n recorder?.stop();\n errorTracker?.stop();\n networkInterceptor?.stop();\n consoleInterceptor?.stop();\n performanceTracker?.stop();\n rageClickDetector?.stop();\n navigationTracker?.stop();\n transport?.stop();\n sessionManager?.end();\n\n recorder = null;\n errorTracker = null;\n networkInterceptor = null;\n consoleInterceptor = null;\n performanceTracker = null;\n rageClickDetector = null;\n navigationTracker = null;\n transport = null;\n sessionManager = null;\n breadcrumbs = null;\n privacyFilter = null;\n config = null;\n}\n\nexport const Ghostplay = {\n VERSION,\n init,\n identify,\n tag,\n setMetadata,\n stop,\n};\n\nexport default Ghostplay;\nexport type { GhostplayConfig, GhostplayEvent, Session } from './types';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/config.ts","../src/session.ts","../src/recorder.ts","../src/modules/error-tracker.ts","../src/modules/network-interceptor.ts","../src/modules/console-interceptor.ts","../src/modules/performance-tracker.ts","../src/modules/rage-click-detector.ts","../src/modules/breadcrumbs.ts","../src/modules/navigation-tracker.ts","../src/privacy.ts","../src/transport.ts","../src/index.ts"],"names":["DEFAULT_CONFIG","resolveConfig","config","generateId","timestamp","random","SessionManager","projectId","userId","traits","session","tag","data","getUnpatchedMutationObserver","iframe","Recorder","sessionId","emit","rrwebConfig","patchedMO","nativeMO","record","event","ErrorTracker","message","source","lineno","colno","error","reason","GHOSTPLAY_URL_PATTERN","NetworkInterceptor","self","input","init","url","method","startTime","requestHeaders","extractHeaders","requestBody","extractBody","response","responseHeaders","value","key","responseBody","contentType","text","headers","result","body","LEVELS","ConsoleInterceptor","level","args","arg","THRESHOLDS","PerformanceTracker","o","metric","good","poor","rating","observer","list","entries","last","first","clsValue","entry","RageClickDetector","target","now","r","t","el","testId","className","Breadcrumbs","maxSize","category","crumb","NavigationTracker","trigger","newUrl","from","to","ALWAYS_DENY_RESPONSE_HEADERS","PrivacyFilter","options","denyList","denyLower","h","filtered","patterns","pattern","Transport","eventSize","events","batch","useKeepalive","res","VERSION","sessionManager","recorder","errorTracker","networkInterceptor","consoleInterceptor","performanceTracker","rageClickDetector","breadcrumbs","navigationTracker","privacyFilter","transport","ensureInitialized","handleEvent","userConfig","stop","startRecording","sanitized","identify","tagName","setMetadata","Ghostplay","index_default"],"mappings":"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","file":"index.js","sourcesContent":["// rrweb type aliases — rrweb 2.0.0-alpha.4 does not re-export these from its\n// package root, so we define compatible local aliases to keep the same shape.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype eventWithTime = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype recordOptions<T = eventWithTime> = Record<string, any>;\n\n// --- SDK Config ---\n\nexport interface NetworkSanitizeConfig {\n denyHeaders: string[];\n denyBodyUrls: string[];\n}\n\nexport interface GhostplayConfig {\n projectId: string;\n apiUrl?: string;\n\n // Privacy\n maskAllInputs?: boolean;\n maskInputSelector?: string;\n blockSelector?: string;\n ignoreSelector?: string;\n networkSanitize?: NetworkSanitizeConfig;\n\n // Feature toggles\n recordNetwork?: boolean;\n recordConsole?: boolean;\n trackPerformance?: boolean;\n trackRageClicks?: boolean;\n trackNavigation?: boolean;\n\n // Transport\n flushIntervalMs?: number;\n flushMaxBytes?: number;\n\n // rrweb passthrough\n rrwebConfig?: Partial<recordOptions<eventWithTime>>;\n}\n\nexport const DEFAULT_CONFIG: Required<\n Pick<\n GhostplayConfig,\n | 'apiUrl'\n | 'maskAllInputs'\n | 'recordNetwork'\n | 'recordConsole'\n | 'trackPerformance'\n | 'trackRageClicks'\n | 'trackNavigation'\n | 'flushIntervalMs'\n | 'flushMaxBytes'\n >\n> = {\n apiUrl: 'https://api.ghostplay.dev/v1',\n maskAllInputs: true,\n recordNetwork: true,\n recordConsole: true,\n trackPerformance: true,\n trackRageClicks: true,\n trackNavigation: true,\n flushIntervalMs: 5000,\n flushMaxBytes: 50_000,\n};\n\n// --- Session ---\n\nexport interface Session {\n id: string;\n projectId: string;\n startedAt: number;\n userId?: string;\n userTraits?: Record<string, unknown>;\n tags: string[];\n metadata: Record<string, unknown>;\n}\n\n// --- Events ---\n\nexport type GhostplayEventType =\n | 'rrweb'\n | 'error'\n | 'network'\n | 'console'\n | 'performance'\n | 'rage-click'\n | 'breadcrumb'\n | 'identify'\n | 'tag'\n | 'metadata'\n | 'navigation';\n\nexport interface GhostplayEvent {\n type: GhostplayEventType;\n timestamp: number;\n sessionId: string;\n data: unknown;\n}\n\nexport interface ErrorEventData {\n message: string;\n stack?: string;\n source?: string;\n lineno?: number;\n colno?: number;\n type: 'onerror' | 'unhandledrejection';\n}\n\nexport interface NetworkEventData {\n method: string;\n url: string;\n status?: number;\n statusText?: string;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n startTime: number;\n endTime?: number;\n duration?: number;\n}\n\nexport interface ConsoleEventData {\n level: 'log' | 'warn' | 'error' | 'info' | 'debug';\n args: unknown[];\n}\n\nexport interface PerformanceEventData {\n metric: 'LCP' | 'FID' | 'CLS' | 'TTFB' | 'INP';\n value: number;\n rating: 'good' | 'needs-improvement' | 'poor';\n}\n\nexport interface RageClickEventData {\n selector: string;\n clickCount: number;\n x: number;\n y: number;\n}\n\nexport interface BreadcrumbEventData {\n category: 'navigation' | 'click' | 'input' | 'error' | 'network' | 'custom';\n message: string;\n data?: Record<string, unknown>;\n}\n\nexport interface NavigationEventData {\n from: string | null;\n to: string;\n trigger: 'pushState' | 'replaceState' | 'popstate' | 'hashchange' | 'initial';\n}\n\n// --- Transport ---\n\nexport interface EventBatch {\n projectId: string;\n sessionId: string;\n events: GhostplayEvent[];\n sentAt: number;\n}\n","import { type GhostplayConfig, DEFAULT_CONFIG } from './types';\n\nexport type ResolvedConfig = GhostplayConfig & typeof DEFAULT_CONFIG;\n\nexport function resolveConfig(config: GhostplayConfig): ResolvedConfig {\n if (!config.projectId) {\n throw new Error('[Ghostplay] projectId is required');\n }\n if (!config.projectId.startsWith('gp_')) {\n throw new Error('[Ghostplay] projectId must start with \"gp_\"');\n }\n\n return { ...DEFAULT_CONFIG, ...config };\n}\n","import type { Session } from './types';\n\nfunction generateId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 10);\n return `gs_${timestamp}_${random}`;\n}\n\nexport class SessionManager {\n private session: Session | null = null;\n\n constructor(private projectId: string) {}\n\n start(): Session {\n this.session = {\n id: generateId(),\n projectId: this.projectId,\n startedAt: Date.now(),\n tags: [],\n metadata: {},\n };\n return this.session;\n }\n\n current(): Session {\n if (!this.session) {\n throw new Error('[Ghostplay] No active session');\n }\n return this.session;\n }\n\n identify(userId: string, traits?: Record<string, unknown>): void {\n const session = this.current();\n session.userId = userId;\n session.userTraits = traits;\n }\n\n addTag(tag: string): void {\n const session = this.current();\n if (!session.tags.includes(tag)) {\n session.tags.push(tag);\n }\n }\n\n setMetadata(data: Record<string, unknown>): void {\n const session = this.current();\n session.metadata = { ...session.metadata, ...data };\n }\n\n end(): void {\n this.session = null;\n }\n}\n","import { record } from 'rrweb';\nimport type { GhostplayEvent } from './types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\n/**\n * Get a truly unpatched MutationObserver by extracting it from a\n * hidden iframe. Third-party replay tools (LogRocket, FullStory,\n * Datadog RUM) load via script tags in <head> and monkey-patch\n * window.MutationObserver BEFORE our bundle executes. An iframe has\n * its own set of built-in constructors that are never patched.\n */\nfunction getUnpatchedMutationObserver(): typeof MutationObserver | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const iframe = document.createElement('iframe');\n iframe.style.display = 'none';\n iframe.setAttribute('aria-hidden', 'true');\n iframe.setAttribute('data-ghostplay', 'mo');\n document.documentElement.appendChild(iframe);\n return (iframe.contentWindow as any)?.MutationObserver;\n // iframe stays alive — its MO prototype must remain reachable\n } catch {\n return undefined;\n }\n}\n\nexport class Recorder {\n private stopFn: (() => void) | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback,\n private rrwebConfig?: Record<string, any>\n ) {}\n\n start(): void {\n // Swap in the unpatched MutationObserver for the duration of\n // record() so rrweb creates its observer from the native\n // constructor. Restore the original afterwards so other tools\n // (LogRocket etc.) keep working.\n const patchedMO = window.MutationObserver;\n const nativeMO = getUnpatchedMutationObserver();\n if (nativeMO) {\n window.MutationObserver = nativeMO;\n }\n\n this.stopFn = record({\n ...this.rrwebConfig,\n emit: (event: any) => {\n this.emit({\n type: 'rrweb',\n timestamp: event.timestamp,\n sessionId: this.sessionId,\n data: event,\n });\n },\n }) ?? null;\n\n window.MutationObserver = patchedMO;\n }\n\n /** Force a new FullSnapshot (useful as a one-time safety net for\n * async content that wasn't in the initial snapshot). */\n takeFullSnapshot(): void {\n if (this.stopFn) {\n record.takeFullSnapshot();\n }\n }\n\n stop(): void {\n if (this.stopFn) {\n this.stopFn();\n this.stopFn = null;\n }\n }\n}\n","import type { GhostplayEvent, ErrorEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nexport class ErrorTracker {\n private onErrorHandler: OnErrorEventHandler | null = null;\n private onRejectionHandler: ((e: PromiseRejectionEvent) => void) | null = null;\n private previousOnError: OnErrorEventHandler | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.previousOnError = window.onerror;\n\n this.onErrorHandler = (message, source, lineno, colno, error) => {\n const data: ErrorEventData = {\n message: String(message),\n stack: error?.stack,\n source: source ?? undefined,\n lineno: lineno ?? undefined,\n colno: colno ?? undefined,\n type: 'onerror',\n };\n\n this.emit({\n type: 'error',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n\n if (this.previousOnError) {\n return this.previousOnError.call(window, message, source, lineno, colno, error);\n }\n return false;\n };\n\n this.onRejectionHandler = (e: PromiseRejectionEvent) => {\n const reason = e.reason;\n const data: ErrorEventData = {\n message:\n reason instanceof Error ? reason.message : String(reason),\n stack: reason instanceof Error ? reason.stack : undefined,\n type: 'unhandledrejection',\n };\n\n this.emit({\n type: 'error',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n };\n\n window.onerror = this.onErrorHandler;\n window.addEventListener('unhandledrejection', this.onRejectionHandler);\n }\n\n stop(): void {\n if (this.previousOnError) {\n window.onerror = this.previousOnError;\n } else {\n window.onerror = null;\n }\n\n if (this.onRejectionHandler) {\n window.removeEventListener('unhandledrejection', this.onRejectionHandler);\n }\n\n this.onErrorHandler = null;\n this.onRejectionHandler = null;\n }\n}\n","import type { GhostplayEvent, NetworkEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nconst GHOSTPLAY_URL_PATTERN = /ghostplay\\.dev/;\nconst MAX_BODY_SIZE = 1024 * 1024; // 1MB limit per body\n\nexport class NetworkInterceptor {\n private originalFetch: typeof fetch | null = null;\n private active = false;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.active = true;\n this.patchFetch();\n }\n\n stop(): void {\n this.active = false;\n this.restoreFetch();\n }\n\n private patchFetch(): void {\n this.originalFetch = globalThis.fetch;\n const self = this;\n\n globalThis.fetch = async function (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n const method = init?.method ?? 'GET';\n\n if (GHOSTPLAY_URL_PATTERN.test(url)) {\n return self.originalFetch!.call(globalThis, input, init);\n }\n\n const startTime = Date.now();\n\n // Extract request headers\n const requestHeaders = extractHeaders(init?.headers);\n\n // Extract request body\n const requestBody = extractBody(init?.body);\n\n try {\n const response = await self.originalFetch!.call(\n globalThis,\n input,\n init\n );\n\n if (self.active) {\n // Extract response headers\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n // Clone response to read body without consuming the original\n let responseBody: string | undefined;\n try {\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('json') || contentType.includes('text')) {\n const clone = response.clone();\n const text = await clone.text();\n responseBody = text.length > MAX_BODY_SIZE\n ? text.substring(0, MAX_BODY_SIZE) + '...[truncated]'\n : text;\n }\n } catch {\n // Body read failed — skip\n }\n\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n status: response.status,\n statusText: response.statusText,\n requestHeaders,\n responseHeaders,\n requestBody,\n responseBody,\n startTime,\n endTime: Date.now(),\n duration: Date.now() - startTime,\n };\n\n self.emit({\n type: 'network',\n timestamp: startTime,\n sessionId: self.sessionId,\n data,\n });\n }\n\n return response;\n } catch (error) {\n if (self.active) {\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n requestHeaders,\n requestBody,\n startTime,\n endTime: Date.now(),\n duration: Date.now() - startTime,\n };\n\n self.emit({\n type: 'network',\n timestamp: startTime,\n sessionId: self.sessionId,\n data,\n });\n }\n\n throw error;\n }\n };\n }\n\n private restoreFetch(): void {\n if (this.originalFetch) {\n globalThis.fetch = this.originalFetch;\n this.originalFetch = null;\n }\n }\n}\n\nfunction extractHeaders(headers: HeadersInit | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => { result[key] = value; });\n } else if (Array.isArray(headers)) {\n for (const [key, value] of headers) { result[key] = value; }\n } else {\n for (const [key, value] of Object.entries(headers)) { result[key] = value; }\n }\n\n return result;\n}\n\nfunction extractBody(body: BodyInit | null | undefined): string | undefined {\n if (!body) return undefined;\n\n if (typeof body === 'string') {\n return body.length > MAX_BODY_SIZE\n ? body.substring(0, MAX_BODY_SIZE) + '...[truncated]'\n : body;\n }\n\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n\n // FormData, Blob, ArrayBuffer, ReadableStream — skip (too complex/binary)\n return undefined;\n}\n","import type { GhostplayEvent, ConsoleEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\ntype ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug'];\n\nexport class ConsoleInterceptor {\n private originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {};\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n for (const level of LEVELS) {\n this.originals[level] = console[level].bind(console);\n console[level] = (...args: unknown[]) => {\n const data: ConsoleEventData = {\n level,\n args: args.map((arg) => this.serialize(arg)),\n };\n this.emit({\n type: 'console',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n this.originals[level]?.(...args);\n };\n }\n }\n\n stop(): void {\n for (const level of LEVELS) {\n if (this.originals[level]) {\n console[level] = this.originals[level] as any;\n }\n }\n this.originals = {};\n }\n\n private serialize(arg: unknown): unknown {\n if (arg instanceof Error) {\n return { __type: 'Error', message: arg.message, stack: arg.stack };\n }\n if (typeof arg === 'object' && arg !== null) {\n try { JSON.stringify(arg); return arg; } catch { return String(arg); }\n }\n return arg;\n }\n}\n","import type { GhostplayEvent, PerformanceEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\ntype Metric = PerformanceEventData['metric'];\n\nconst THRESHOLDS: Record<Metric, [number, number]> = {\n LCP: [1000, 4000],\n FID: [100, 300],\n CLS: [0.1, 0.25],\n TTFB: [800, 1800],\n INP: [200, 500],\n};\n\nexport class PerformanceTracker {\n private observers: PerformanceObserver[] = [];\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n if (typeof PerformanceObserver === 'undefined') return;\n this.observeLCP();\n this.observeFID();\n this.observeCLS();\n this.observeTTFB();\n this.observeINP();\n }\n\n stop(): void {\n this.observers.forEach((o) => o.disconnect());\n this.observers = [];\n }\n\n handleMetric(metric: Metric, value: number): void {\n const [good, poor] = THRESHOLDS[metric];\n const rating: PerformanceEventData['rating'] =\n value <= good ? 'good' : value <= poor ? 'needs-improvement' : 'poor';\n this.emit({\n type: 'performance',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data: { metric, value, rating },\n });\n }\n\n private observeLCP(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n const last = entries[entries.length - 1];\n if (last) this.handleMetric('LCP', last.startTime);\n });\n observer.observe({ type: 'largest-contentful-paint', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeFID(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n const first = entries[0] as any;\n if (first) this.handleMetric('FID', first.processingStart - first.startTime);\n });\n observer.observe({ type: 'first-input', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeCLS(): void {\n let clsValue = 0;\n try {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as any[]) {\n if (!entry.hadRecentInput) clsValue += entry.value;\n }\n this.handleMetric('CLS', clsValue);\n });\n observer.observe({ type: 'layout-shift', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeTTFB(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as any;\n if (entry) this.handleMetric('TTFB', entry.responseStart);\n });\n observer.observe({ type: 'navigation', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n\n private observeINP(): void {\n try {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as any[]) {\n this.handleMetric('INP', entry.duration);\n }\n });\n observer.observe({ type: 'event', buffered: true });\n this.observers.push(observer);\n } catch {}\n }\n}\n","import type { GhostplayEvent, RageClickEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nconst RAGE_THRESHOLD = 3;\nconst RAGE_WINDOW_MS = 1000;\n\ninterface ClickRecord {\n element: Element;\n timestamps: number[];\n}\n\nexport class RageClickDetector {\n private clickHandler: ((e: MouseEvent) => void) | null = null;\n private recentClicks: ClickRecord[] = [];\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.clickHandler = (e: MouseEvent) => {\n const target = e.target as Element;\n if (!target) return;\n const now = Date.now();\n let record = this.recentClicks.find((r) => r.element === target);\n if (!record) {\n record = { element: target, timestamps: [] };\n this.recentClicks.push(record);\n }\n record.timestamps.push(now);\n record.timestamps = record.timestamps.filter((t) => now - t < RAGE_WINDOW_MS);\n if (record.timestamps.length >= RAGE_THRESHOLD) {\n const data: RageClickEventData = {\n selector: this.getSelector(target),\n clickCount: record.timestamps.length,\n x: e.clientX,\n y: e.clientY,\n };\n this.emit({\n type: 'rage-click',\n timestamp: now,\n sessionId: this.sessionId,\n data,\n });\n record.timestamps = [];\n }\n this.recentClicks = this.recentClicks.filter((r) => r.timestamps.length > 0);\n };\n document.addEventListener('click', this.clickHandler, true);\n }\n\n stop(): void {\n if (this.clickHandler) {\n document.removeEventListener('click', this.clickHandler, true);\n this.clickHandler = null;\n }\n this.recentClicks = [];\n }\n\n private getSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const testId = el.getAttribute('data-testid');\n if (testId) return `[data-testid=\"${testId}\"]`;\n const tag = el.tagName.toLowerCase();\n const className = el.className\n ? `.${el.className.split(' ').filter(Boolean).join('.')}`\n : '';\n return `${tag}${className}`;\n }\n}\n","import type { BreadcrumbEventData } from '../types';\n\nexport class Breadcrumbs {\n private trail: BreadcrumbEventData[] = [];\n\n constructor(private maxSize: number = 50) {}\n\n add(\n category: BreadcrumbEventData['category'],\n message: string,\n data?: Record<string, unknown>\n ): void {\n const crumb: BreadcrumbEventData = { category, message };\n if (data) crumb.data = data;\n this.trail.push(crumb);\n if (this.trail.length > this.maxSize) {\n this.trail = this.trail.slice(-this.maxSize);\n }\n }\n\n getTrail(): BreadcrumbEventData[] {\n return [...this.trail];\n }\n\n clear(): void {\n this.trail = [];\n }\n}\n","import type { GhostplayEvent, NavigationEventData } from '../types';\n\ntype EventCallback = (event: GhostplayEvent) => void;\n\nexport class NavigationTracker {\n private currentUrl: string = '';\n private originalPushState: typeof history.pushState | null = null;\n private originalReplaceState: typeof history.replaceState | null = null;\n private popstateHandler: (() => void) | null = null;\n private hashchangeHandler: (() => void) | null = null;\n\n constructor(\n private sessionId: string,\n private emit: EventCallback\n ) {}\n\n start(): void {\n this.currentUrl = window.location.href;\n\n // Emit initial navigation event\n this.emitNavigation(null, this.currentUrl, 'initial');\n\n // Patch history.pushState\n this.originalPushState = history.pushState.bind(history);\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this.originalPushState!(...args);\n this.handleUrlChange('pushState');\n };\n\n // Patch history.replaceState\n this.originalReplaceState = history.replaceState.bind(history);\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this.originalReplaceState!(...args);\n this.handleUrlChange('replaceState');\n };\n\n // Listen for popstate (back/forward)\n this.popstateHandler = () => this.handleUrlChange('popstate');\n window.addEventListener('popstate', this.popstateHandler);\n\n // Listen for hashchange\n this.hashchangeHandler = () => this.handleUrlChange('hashchange');\n window.addEventListener('hashchange', this.hashchangeHandler);\n }\n\n stop(): void {\n if (this.originalPushState) {\n history.pushState = this.originalPushState;\n this.originalPushState = null;\n }\n if (this.originalReplaceState) {\n history.replaceState = this.originalReplaceState;\n this.originalReplaceState = null;\n }\n if (this.popstateHandler) {\n window.removeEventListener('popstate', this.popstateHandler);\n this.popstateHandler = null;\n }\n if (this.hashchangeHandler) {\n window.removeEventListener('hashchange', this.hashchangeHandler);\n this.hashchangeHandler = null;\n }\n }\n\n private handleUrlChange(trigger: NavigationEventData['trigger']): void {\n const newUrl = window.location.href;\n if (newUrl !== this.currentUrl) {\n const from = this.currentUrl;\n this.currentUrl = newUrl;\n this.emitNavigation(from, newUrl, trigger);\n }\n }\n\n private emitNavigation(from: string | null, to: string, trigger: NavigationEventData['trigger']): void {\n const data: NavigationEventData = { from, to, trigger };\n this.emit({\n type: 'navigation',\n timestamp: Date.now(),\n sessionId: this.sessionId,\n data,\n });\n }\n}\n","import type { GhostplayConfig, NetworkEventData } from './types';\n\nconst ALWAYS_DENY_RESPONSE_HEADERS = ['set-cookie'];\n\ninterface PrivacyOptions {\n networkSanitize?: GhostplayConfig['networkSanitize'];\n}\n\nexport class PrivacyFilter {\n constructor(private options: PrivacyOptions) {}\n\n sanitizeNetworkEvent(event: NetworkEventData): NetworkEventData {\n const result = { ...event };\n const config = this.options.networkSanitize;\n\n // Always remove sensitive response headers\n if (result.responseHeaders) {\n result.responseHeaders = this.filterHeaders(\n result.responseHeaders,\n ALWAYS_DENY_RESPONSE_HEADERS\n );\n }\n\n if (!config) return result;\n\n // Remove denied request headers\n if (result.requestHeaders && config.denyHeaders.length > 0) {\n result.requestHeaders = this.filterHeaders(\n result.requestHeaders,\n config.denyHeaders\n );\n }\n\n // Remove denied response headers\n if (result.responseHeaders && config.denyHeaders.length > 0) {\n result.responseHeaders = this.filterHeaders(\n result.responseHeaders,\n config.denyHeaders\n );\n }\n\n // Redact body for denied URLs\n if (config.denyBodyUrls.length > 0 && this.urlMatchesDeny(event.url, config.denyBodyUrls)) {\n if (result.requestBody) result.requestBody = '[REDACTED]';\n if (result.responseBody) result.responseBody = '[REDACTED]';\n }\n\n return result;\n }\n\n private filterHeaders(\n headers: Record<string, string>,\n denyList: string[]\n ): Record<string, string> {\n const denyLower = denyList.map((h) => h.toLowerCase());\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (!denyLower.includes(key.toLowerCase())) {\n filtered[key] = value;\n }\n }\n return filtered;\n }\n\n private urlMatchesDeny(url: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n const regex = new RegExp(\n '^' + pattern.replace(/\\*/g, '.*') + '$'\n );\n return regex.test(url);\n });\n }\n}\n","import type { GhostplayEvent, EventBatch } from './types';\n\ninterface TransportConfig {\n apiUrl: string;\n projectId: string;\n sessionId: string;\n flushIntervalMs: number;\n flushMaxBytes: number;\n}\n\nconst KEEPALIVE_LIMIT = 60_000; // Stay under 64KB browser limit\n\nexport class Transport {\n private buffer: GhostplayEvent[] = [];\n private bufferBytes = 0;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(private config: TransportConfig) {}\n\n start(): void {\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushIntervalMs);\n }\n\n stop(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n this.flush();\n }\n\n send(event: GhostplayEvent): void {\n const eventSize = JSON.stringify(event).length;\n this.buffer.push(event);\n this.bufferBytes += eventSize;\n if (this.bufferBytes >= this.config.flushMaxBytes) {\n this.flush();\n }\n }\n\n private flush(): void {\n if (this.buffer.length === 0) return;\n const events = this.buffer;\n this.buffer = [];\n this.bufferBytes = 0;\n const batch: EventBatch = {\n projectId: this.config.projectId,\n sessionId: this.config.sessionId,\n events,\n sentAt: Date.now(),\n };\n const body = JSON.stringify(batch);\n const url = `${this.config.apiUrl}/events`;\n\n // Use keepalive only for small payloads (Safari enforces 64KB limit strictly)\n const useKeepalive = body.length < KEEPALIVE_LIMIT;\n\n try {\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-project-id': this.config.projectId,\n },\n body,\n keepalive: useKeepalive,\n })\n .then((res) => {\n if (!res.ok) {\n console.warn(`[Ghostplay] Event batch rejected (${res.status})`);\n }\n })\n .catch(() => {\n // Fallback to sendBeacon\n this.sendBeacon(url, body);\n });\n } catch {\n this.sendBeacon(url, body);\n }\n }\n\n private sendBeacon(url: string, body: string): void {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }));\n }\n }\n}\n","import { resolveConfig, type ResolvedConfig } from './config';\nimport { SessionManager } from './session';\nimport { Recorder } from './recorder';\nimport { ErrorTracker } from './modules/error-tracker';\nimport { NetworkInterceptor } from './modules/network-interceptor';\nimport { ConsoleInterceptor } from './modules/console-interceptor';\nimport { PerformanceTracker } from './modules/performance-tracker';\nimport { RageClickDetector } from './modules/rage-click-detector';\nimport { Breadcrumbs } from './modules/breadcrumbs';\nimport { NavigationTracker } from './modules/navigation-tracker';\nimport { PrivacyFilter } from './privacy';\nimport { Transport } from './transport';\nimport type { GhostplayConfig, GhostplayEvent } from './types';\n\nexport const VERSION = '0.1.0';\n\nlet config: ResolvedConfig | null = null;\nlet sessionManager: SessionManager | null = null;\nlet recorder: Recorder | null = null;\nlet errorTracker: ErrorTracker | null = null;\nlet networkInterceptor: NetworkInterceptor | null = null;\nlet consoleInterceptor: ConsoleInterceptor | null = null;\nlet performanceTracker: PerformanceTracker | null = null;\nlet rageClickDetector: RageClickDetector | null = null;\nlet breadcrumbs: Breadcrumbs | null = null;\nlet navigationTracker: NavigationTracker | null = null;\nlet privacyFilter: PrivacyFilter | null = null;\nlet transport: Transport | null = null;\n\nfunction ensureInitialized(): void {\n if (!sessionManager) {\n throw new Error('[Ghostplay] SDK not initialized. Call Ghostplay.init() first.');\n }\n}\n\nfunction handleEvent(event: GhostplayEvent): void {\n if (breadcrumbs && event.type !== 'rrweb') {\n breadcrumbs.add(\n event.type === 'error'\n ? 'error'\n : event.type === 'network'\n ? 'network'\n : event.type === 'rage-click'\n ? 'click'\n : 'custom',\n `${event.type}: ${JSON.stringify(event.data).substring(0, 100)}`\n );\n }\n transport?.send(event);\n}\n\nfunction init(userConfig: GhostplayConfig): void {\n stop();\n config = resolveConfig(userConfig);\n sessionManager = new SessionManager(config.projectId);\n const session = sessionManager.start();\n\n privacyFilter = new PrivacyFilter({\n networkSanitize: config.networkSanitize,\n });\n\n transport = new Transport({\n apiUrl: config.apiUrl,\n projectId: config.projectId,\n sessionId: session.id,\n flushIntervalMs: config.flushIntervalMs,\n flushMaxBytes: config.flushMaxBytes,\n });\n transport.start();\n\n breadcrumbs = new Breadcrumbs();\n\n recorder = new Recorder(session.id, handleEvent, {\n maskAllInputs: config.maskAllInputs,\n ...(config.maskInputSelector ? { maskInputSelector: config.maskInputSelector } : {}),\n ...(config.blockSelector ? { blockSelector: config.blockSelector } : {}),\n ...(config.ignoreSelector ? { ignoreSelector: config.ignoreSelector } : {}),\n ...config.rrwebConfig,\n });\n\n // Defer recording start so the initial FullSnapshot captures the\n // rendered DOM shell rather than an empty SPA root element.\n const startRecording = () => {\n recorder?.start();\n // One-time follow-up snapshot to capture async-loaded content\n // (data fetched via API after initial render). Runs once, not\n // periodically — avoids the storage overhead of checkoutEveryNms.\n setTimeout(() => recorder?.takeFullSnapshot(), 3000);\n };\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => requestAnimationFrame(startRecording));\n } else {\n window.addEventListener('load', () =>\n requestAnimationFrame(startRecording), { once: true });\n }\n\n errorTracker = new ErrorTracker(session.id, handleEvent);\n errorTracker.start();\n\n if (config.recordNetwork) {\n networkInterceptor = new NetworkInterceptor(session.id, (event) => {\n const sanitized = privacyFilter!.sanitizeNetworkEvent(event.data as any);\n handleEvent({ ...event, data: sanitized });\n });\n networkInterceptor.start();\n }\n\n if (config.recordConsole) {\n consoleInterceptor = new ConsoleInterceptor(session.id, handleEvent);\n consoleInterceptor.start();\n }\n\n if (config.trackPerformance) {\n performanceTracker = new PerformanceTracker(session.id, handleEvent);\n performanceTracker.start();\n }\n\n if (config.trackRageClicks) {\n rageClickDetector = new RageClickDetector(session.id, handleEvent);\n rageClickDetector.start();\n }\n\n if (config.trackNavigation) {\n navigationTracker = new NavigationTracker(session.id, handleEvent);\n navigationTracker.start();\n }\n}\n\nfunction identify(userId: string, traits?: Record<string, unknown>): void {\n ensureInitialized();\n sessionManager!.identify(userId, traits);\n handleEvent({\n type: 'identify',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data: { userId, traits },\n });\n}\n\nfunction tag(tagName: string): void {\n ensureInitialized();\n sessionManager!.addTag(tagName);\n handleEvent({\n type: 'tag',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data: { tag: tagName },\n });\n}\n\nfunction setMetadata(data: Record<string, unknown>): void {\n ensureInitialized();\n sessionManager!.setMetadata(data);\n handleEvent({\n type: 'metadata',\n timestamp: Date.now(),\n sessionId: sessionManager!.current().id,\n data,\n });\n}\n\nfunction stop(): void {\n recorder?.stop();\n errorTracker?.stop();\n networkInterceptor?.stop();\n consoleInterceptor?.stop();\n performanceTracker?.stop();\n rageClickDetector?.stop();\n navigationTracker?.stop();\n transport?.stop();\n sessionManager?.end();\n\n recorder = null;\n errorTracker = null;\n networkInterceptor = null;\n consoleInterceptor = null;\n performanceTracker = null;\n rageClickDetector = null;\n navigationTracker = null;\n transport = null;\n sessionManager = null;\n breadcrumbs = null;\n privacyFilter = null;\n config = null;\n}\n\nexport const Ghostplay = {\n VERSION,\n init,\n identify,\n tag,\n setMetadata,\n stop,\n};\n\nexport default Ghostplay;\nexport type { GhostplayConfig, GhostplayEvent, Session } from './types';\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {record}from'rrweb';var A={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,trackNavigation:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function _(n){if(!n.projectId)throw new Error("[Ghostplay] projectId is required");if(!n.projectId.startsWith("gp_"))throw new Error('[Ghostplay] projectId must start with "gp_"');return {...A,...n}}function Y(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var f=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:Y(),projectId:this.projectId,startedAt:Date.now(),tags:[],metadata:{}},this.session}current(){if(!this.session)throw new Error("[Ghostplay] No active session");return this.session}identify(e,t){let r=this.current();r.userId=e,r.userTraits=t;}addTag(e){let t=this.current();t.tags.includes(e)||t.tags.push(e);}setMetadata(e){let t=this.current();t.metadata={...t.metadata,...e};}end(){this.session=null;}};var z=typeof window<"u"?window.MutationObserver:void 0,g=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){let e=typeof window<"u"?window.MutationObserver:void 0;z&&(window.MutationObserver=z),this.stopFn=record({...this.rrwebConfig,emit:t=>{this.emit({type:"rrweb",timestamp:t.timestamp,sessionId:this.sessionId,data:t});}})??null,e&&(window.MutationObserver=e);}takeFullSnapshot(){this.stopFn&&record.takeFullSnapshot();}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.onErrorHandler=null;this.onRejectionHandler=null;this.previousOnError=null;}start(){this.previousOnError=window.onerror,this.onErrorHandler=(e,t,r,s,i)=>{let a={message:String(e),stack:i?.stack,source:t??void 0,lineno:r??void 0,colno:s??void 0,type:"onerror"};return this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:a}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,i):false},this.onRejectionHandler=e=>{let t=e.reason,r={message:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,type:"unhandledrejection"};this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:r});},window.onerror=this.onErrorHandler,window.addEventListener("unhandledrejection",this.onRejectionHandler);}stop(){this.previousOnError?window.onerror=this.previousOnError:window.onerror=null,this.onRejectionHandler&&window.removeEventListener("unhandledrejection",this.onRejectionHandler),this.onErrorHandler=null,this.onRejectionHandler=null;}};var X=/ghostplay\.dev/;var y=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originalFetch=null;this.active=false;}start(){this.active=true,this.patchFetch();}stop(){this.active=false,this.restoreFetch();}patchFetch(){this.originalFetch=globalThis.fetch;let e=this;globalThis.fetch=async function(t,r){let s=typeof t=="string"?t:t instanceof URL?t.toString():t.url,i=r?.method??"GET";if(X.test(s))return e.originalFetch.call(globalThis,t,r);let a=Date.now(),O=V(r?.headers),L=Z(r?.body);try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let u={};p.headers.forEach((v,j)=>{u[j]=v;});let F;try{let v=p.headers.get("content-type")??"";if(v.includes("json")||v.includes("text")){let G=await p.clone().text();F=G.length>1048576?G.substring(0,1048576)+"...[truncated]":G;}}catch{}let W={method:i.toUpperCase(),url:s,status:p.status,statusText:p.statusText,requestHeaders:O,responseHeaders:u,requestBody:L,responseBody:F,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:W});}return p}catch(p){if(e.active){let u={method:i.toUpperCase(),url:s,requestHeaders:O,requestBody:L,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:u});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};function V(n){let e={};if(!n)return e;if(n instanceof Headers)n.forEach((t,r)=>{e[r]=t;});else if(Array.isArray(n))for(let[t,r]of n)e[t]=r;else for(let[t,r]of Object.entries(n))e[t]=r;return e}function Z(n){if(n){if(typeof n=="string")return n.length>1048576?n.substring(0,1048576)+"...[truncated]":n;if(n instanceof URLSearchParams)return n.toString()}}var q=["log","warn","error","info","debug"],E=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of q)this.originals[e]=console[e].bind(console),console[e]=(...t)=>{let r={level:e,args:t.map(s=>this.serialize(s))};this.emit({type:"console",timestamp:Date.now(),sessionId:this.sessionId,data:r}),this.originals[e]?.(...t);};}stop(){for(let e of q)this.originals[e]&&(console[e]=this.originals[e]);this.originals={};}serialize(e){if(e instanceof Error)return {__type:"Error",message:e.message,stack:e.stack};if(typeof e=="object"&&e!==null)try{return JSON.stringify(e),e}catch{return String(e)}return e}};var J={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},w=class{constructor(e,t){this.sessionId=e;this.emit=t;this.observers=[];}start(){typeof PerformanceObserver>"u"||(this.observeLCP(),this.observeFID(),this.observeCLS(),this.observeTTFB(),this.observeINP());}stop(){this.observers.forEach(e=>e.disconnect()),this.observers=[];}handleMetric(e,t){let[r,s]=J[e],i=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:i}});}observeLCP(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries(),s=r[r.length-1];s&&this.handleMetric("LCP",s.startTime);});e.observe({type:"largest-contentful-paint",buffered:!0}),this.observers.push(e);}catch{}}observeFID(){try{let e=new PerformanceObserver(t=>{let s=t.getEntries()[0];s&&this.handleMetric("FID",s.processingStart-s.startTime);});e.observe({type:"first-input",buffered:!0}),this.observers.push(e);}catch{}}observeCLS(){let e=0;try{let t=new PerformanceObserver(r=>{for(let s of r.getEntries())s.hadRecentInput||(e+=s.value);this.handleMetric("CLS",e);});t.observe({type:"layout-shift",buffered:!0}),this.observers.push(t);}catch{}}observeTTFB(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries()[0];r&&this.handleMetric("TTFB",r.responseStart);});e.observe({type:"navigation",buffered:!0}),this.observers.push(e);}catch{}}observeINP(){try{let e=new PerformanceObserver(t=>{for(let r of t.getEntries())this.handleMetric("INP",r.duration);});e.observe({type:"event",buffered:!0}),this.observers.push(e);}catch{}}};var b=class{constructor(e,t){this.sessionId=e;this.emit=t;this.clickHandler=null;this.recentClicks=[];}start(){this.clickHandler=e=>{let t=e.target;if(!t)return;let r=Date.now(),s=this.recentClicks.find(i=>i.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(i=>r-i<1e3),s.timestamps.length>=3){let i={selector:this.getSelector(t),clickCount:s.timestamps.length,x:e.clientX,y:e.clientY};this.emit({type:"rage-click",timestamp:r,sessionId:this.sessionId,data:i}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(i=>i.timestamps.length>0);},document.addEventListener("click",this.clickHandler,true);}stop(){this.clickHandler&&(document.removeEventListener("click",this.clickHandler,true),this.clickHandler=null),this.recentClicks=[];}getSelector(e){if(e.id)return `#${e.id}`;let t=e.getAttribute("data-testid");if(t)return `[data-testid="${t}"]`;let r=e.tagName.toLowerCase(),s=e.className?`.${e.className.split(" ").filter(Boolean).join(".")}`:"";return `${r}${s}`}};var k=class{constructor(e=50){this.maxSize=e;this.trail=[];}add(e,t,r){let s={category:e,message:t};r&&(s.data=r),this.trail.push(s),this.trail.length>this.maxSize&&(this.trail=this.trail.slice(-this.maxSize));}getTrail(){return [...this.trail]}clear(){this.trail=[];}};var I=class{constructor(e,t){this.sessionId=e;this.emit=t;this.currentUrl="";this.originalPushState=null;this.originalReplaceState=null;this.popstateHandler=null;this.hashchangeHandler=null;}start(){this.currentUrl=window.location.href,this.emitNavigation(null,this.currentUrl,"initial"),this.originalPushState=history.pushState.bind(history),history.pushState=(...e)=>{this.originalPushState(...e),this.handleUrlChange("pushState");},this.originalReplaceState=history.replaceState.bind(history),history.replaceState=(...e)=>{this.originalReplaceState(...e),this.handleUrlChange("replaceState");},this.popstateHandler=()=>this.handleUrlChange("popstate"),window.addEventListener("popstate",this.popstateHandler),this.hashchangeHandler=()=>this.handleUrlChange("hashchange"),window.addEventListener("hashchange",this.hashchangeHandler);}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.hashchangeHandler&&(window.removeEventListener("hashchange",this.hashchangeHandler),this.hashchangeHandler=null);}handleUrlChange(e){let t=window.location.href;if(t!==this.currentUrl){let r=this.currentUrl;this.currentUrl=t,this.emitNavigation(r,t,e);}}emitNavigation(e,t,r){let s={from:e,to:t,trigger:r};this.emit({type:"navigation",timestamp:Date.now(),sessionId:this.sessionId,data:s});}};var K=["set-cookie"],S=class{constructor(e){this.options=e;}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,K)),r&&(t.requestHeaders&&r.denyHeaders.length>0&&(t.requestHeaders=this.filterHeaders(t.requestHeaders,r.denyHeaders)),t.responseHeaders&&r.denyHeaders.length>0&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,r.denyHeaders)),r.denyBodyUrls.length>0&&this.urlMatchesDeny(e.url,r.denyBodyUrls)&&(t.requestBody&&(t.requestBody="[REDACTED]"),t.responseBody&&(t.responseBody="[REDACTED]"))),t}filterHeaders(e,t){let r=t.map(i=>i.toLowerCase()),s={};for(let[i,a]of Object.entries(e))r.includes(i.toLowerCase())||(s[i]=a);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var C=class{constructor(e){this.config=e;this.buffer=[];this.bufferBytes=0;this.flushTimer=null;}start(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushIntervalMs);}stop(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush();}send(e){let t=JSON.stringify(e).length;this.buffer.push(e),this.bufferBytes+=t,this.bufferBytes>=this.config.flushMaxBytes&&this.flush();}flush(){if(this.buffer.length===0)return;let e=this.buffer;this.buffer=[],this.bufferBytes=0;let t={projectId:this.config.projectId,sessionId:this.config.sessionId,events:e,sentAt:Date.now()},r=JSON.stringify(t),s=`${this.config.apiUrl}/events`,i=r.length<6e4;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:i}).then(a=>{a.ok||console.warn(`[Ghostplay] Event batch rejected (${a.status})`);}).catch(()=>{this.sendBeacon(s,r);});}catch{this.sendBeacon(s,r);}}sendBeacon(e,t){typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(e,new Blob([t],{type:"application/json"}));}};var Q="0.1.0",o=null,l=null,d=null,D=null,R=null,T=null,H=null,x=null,N=null,B=null,P=null,h=null;function M(){if(!l)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){N&&n.type!=="rrweb"&&N.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),h?.send(n);}function ee(n){$(),o=_(n),l=new f(o.projectId);let e=l.start();P=new S({networkSanitize:o.networkSanitize}),h=new C({apiUrl:o.apiUrl,projectId:o.projectId,sessionId:e.id,flushIntervalMs:o.flushIntervalMs,flushMaxBytes:o.flushMaxBytes}),h.start(),N=new k,d=new g(e.id,c,{maskAllInputs:o.maskAllInputs,...o.maskInputSelector?{maskInputSelector:o.maskInputSelector}:{},...o.blockSelector?{blockSelector:o.blockSelector}:{},...o.ignoreSelector?{ignoreSelector:o.ignoreSelector}:{},...o.rrwebConfig});let t=()=>{d?.start(),setTimeout(()=>d?.takeFullSnapshot(),3e3);};document.readyState==="complete"?requestAnimationFrame(()=>requestAnimationFrame(t)):window.addEventListener("load",()=>requestAnimationFrame(t),{once:true}),D=new m(e.id,c),D.start(),o.recordNetwork&&(R=new y(e.id,r=>{let s=P.sanitizeNetworkEvent(r.data);c({...r,data:s});}),R.start()),o.recordConsole&&(T=new E(e.id,c),T.start()),o.trackPerformance&&(H=new w(e.id,c),H.start()),o.trackRageClicks&&(x=new b(e.id,c),x.start()),o.trackNavigation&&(B=new I(e.id,c),B.start());}function te(n,e){M(),l.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:l.current().id,data:{userId:n,traits:e}});}function re(n){M(),l.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:l.current().id,data:{tag:n}});}function ne(n){M(),l.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:l.current().id,data:n});}function $(){d?.stop(),D?.stop(),R?.stop(),T?.stop(),H?.stop(),x?.stop(),B?.stop(),h?.stop(),l?.end(),d=null,D=null,R=null,T=null,H=null,x=null,B=null,h=null,l=null,N=null,P=null,o=null;}var se={VERSION:Q,init:ee,identify:te,tag:re,setMetadata:ne,stop:$},Be=se;
|
|
2
|
-
export{se as Ghostplay,Q as VERSION,
|
|
1
|
+
import {record}from'rrweb';var A={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,trackNavigation:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function _(n){if(!n.projectId)throw new Error("[Ghostplay] projectId is required");if(!n.projectId.startsWith("gp_"))throw new Error('[Ghostplay] projectId must start with "gp_"');return {...A,...n}}function W(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var f=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:W(),projectId:this.projectId,startedAt:Date.now(),tags:[],metadata:{}},this.session}current(){if(!this.session)throw new Error("[Ghostplay] No active session");return this.session}identify(e,t){let r=this.current();r.userId=e,r.userTraits=t;}addTag(e){let t=this.current();t.tags.includes(e)||t.tags.push(e);}setMetadata(e){let t=this.current();t.metadata={...t.metadata,...e};}end(){this.session=null;}};function Y(){if(!(typeof document>"u"))try{let n=document.createElement("iframe");return n.style.display="none",n.setAttribute("aria-hidden","true"),n.setAttribute("data-ghostplay","mo"),document.documentElement.appendChild(n),n.contentWindow?.MutationObserver}catch{return}}var g=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){let e=window.MutationObserver,t=Y();t&&(window.MutationObserver=t),this.stopFn=record({...this.rrwebConfig,emit:r=>{this.emit({type:"rrweb",timestamp:r.timestamp,sessionId:this.sessionId,data:r});}})??null,window.MutationObserver=e;}takeFullSnapshot(){this.stopFn&&record.takeFullSnapshot();}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.onErrorHandler=null;this.onRejectionHandler=null;this.previousOnError=null;}start(){this.previousOnError=window.onerror,this.onErrorHandler=(e,t,r,s,i)=>{let a={message:String(e),stack:i?.stack,source:t??void 0,lineno:r??void 0,colno:s??void 0,type:"onerror"};return this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:a}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,i):false},this.onRejectionHandler=e=>{let t=e.reason,r={message:t instanceof Error?t.message:String(t),stack:t instanceof Error?t.stack:void 0,type:"unhandledrejection"};this.emit({type:"error",timestamp:Date.now(),sessionId:this.sessionId,data:r});},window.onerror=this.onErrorHandler,window.addEventListener("unhandledrejection",this.onRejectionHandler);}stop(){this.previousOnError?window.onerror=this.previousOnError:window.onerror=null,this.onRejectionHandler&&window.removeEventListener("unhandledrejection",this.onRejectionHandler),this.onErrorHandler=null,this.onRejectionHandler=null;}};var X=/ghostplay\.dev/;var y=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originalFetch=null;this.active=false;}start(){this.active=true,this.patchFetch();}stop(){this.active=false,this.restoreFetch();}patchFetch(){this.originalFetch=globalThis.fetch;let e=this;globalThis.fetch=async function(t,r){let s=typeof t=="string"?t:t instanceof URL?t.toString():t.url,i=r?.method??"GET";if(X.test(s))return e.originalFetch.call(globalThis,t,r);let a=Date.now(),P=V(r?.headers),L=Z(r?.body);try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let h={};p.headers.forEach((v,j)=>{h[j]=v;});let F;try{let v=p.headers.get("content-type")??"";if(v.includes("json")||v.includes("text")){let G=await p.clone().text();F=G.length>1048576?G.substring(0,1048576)+"...[truncated]":G;}}catch{}let $={method:i.toUpperCase(),url:s,status:p.status,statusText:p.statusText,requestHeaders:P,responseHeaders:h,requestBody:L,responseBody:F,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:$});}return p}catch(p){if(e.active){let h={method:i.toUpperCase(),url:s,requestHeaders:P,requestBody:L,startTime:a,endTime:Date.now(),duration:Date.now()-a};e.emit({type:"network",timestamp:a,sessionId:e.sessionId,data:h});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};function V(n){let e={};if(!n)return e;if(n instanceof Headers)n.forEach((t,r)=>{e[r]=t;});else if(Array.isArray(n))for(let[t,r]of n)e[t]=r;else for(let[t,r]of Object.entries(n))e[t]=r;return e}function Z(n){if(n){if(typeof n=="string")return n.length>1048576?n.substring(0,1048576)+"...[truncated]":n;if(n instanceof URLSearchParams)return n.toString()}}var z=["log","warn","error","info","debug"],E=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of z)this.originals[e]=console[e].bind(console),console[e]=(...t)=>{let r={level:e,args:t.map(s=>this.serialize(s))};this.emit({type:"console",timestamp:Date.now(),sessionId:this.sessionId,data:r}),this.originals[e]?.(...t);};}stop(){for(let e of z)this.originals[e]&&(console[e]=this.originals[e]);this.originals={};}serialize(e){if(e instanceof Error)return {__type:"Error",message:e.message,stack:e.stack};if(typeof e=="object"&&e!==null)try{return JSON.stringify(e),e}catch{return String(e)}return e}};var J={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},w=class{constructor(e,t){this.sessionId=e;this.emit=t;this.observers=[];}start(){typeof PerformanceObserver>"u"||(this.observeLCP(),this.observeFID(),this.observeCLS(),this.observeTTFB(),this.observeINP());}stop(){this.observers.forEach(e=>e.disconnect()),this.observers=[];}handleMetric(e,t){let[r,s]=J[e],i=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:i}});}observeLCP(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries(),s=r[r.length-1];s&&this.handleMetric("LCP",s.startTime);});e.observe({type:"largest-contentful-paint",buffered:!0}),this.observers.push(e);}catch{}}observeFID(){try{let e=new PerformanceObserver(t=>{let s=t.getEntries()[0];s&&this.handleMetric("FID",s.processingStart-s.startTime);});e.observe({type:"first-input",buffered:!0}),this.observers.push(e);}catch{}}observeCLS(){let e=0;try{let t=new PerformanceObserver(r=>{for(let s of r.getEntries())s.hadRecentInput||(e+=s.value);this.handleMetric("CLS",e);});t.observe({type:"layout-shift",buffered:!0}),this.observers.push(t);}catch{}}observeTTFB(){try{let e=new PerformanceObserver(t=>{let r=t.getEntries()[0];r&&this.handleMetric("TTFB",r.responseStart);});e.observe({type:"navigation",buffered:!0}),this.observers.push(e);}catch{}}observeINP(){try{let e=new PerformanceObserver(t=>{for(let r of t.getEntries())this.handleMetric("INP",r.duration);});e.observe({type:"event",buffered:!0}),this.observers.push(e);}catch{}}};var b=class{constructor(e,t){this.sessionId=e;this.emit=t;this.clickHandler=null;this.recentClicks=[];}start(){this.clickHandler=e=>{let t=e.target;if(!t)return;let r=Date.now(),s=this.recentClicks.find(i=>i.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(i=>r-i<1e3),s.timestamps.length>=3){let i={selector:this.getSelector(t),clickCount:s.timestamps.length,x:e.clientX,y:e.clientY};this.emit({type:"rage-click",timestamp:r,sessionId:this.sessionId,data:i}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(i=>i.timestamps.length>0);},document.addEventListener("click",this.clickHandler,true);}stop(){this.clickHandler&&(document.removeEventListener("click",this.clickHandler,true),this.clickHandler=null),this.recentClicks=[];}getSelector(e){if(e.id)return `#${e.id}`;let t=e.getAttribute("data-testid");if(t)return `[data-testid="${t}"]`;let r=e.tagName.toLowerCase(),s=e.className?`.${e.className.split(" ").filter(Boolean).join(".")}`:"";return `${r}${s}`}};var k=class{constructor(e=50){this.maxSize=e;this.trail=[];}add(e,t,r){let s={category:e,message:t};r&&(s.data=r),this.trail.push(s),this.trail.length>this.maxSize&&(this.trail=this.trail.slice(-this.maxSize));}getTrail(){return [...this.trail]}clear(){this.trail=[];}};var I=class{constructor(e,t){this.sessionId=e;this.emit=t;this.currentUrl="";this.originalPushState=null;this.originalReplaceState=null;this.popstateHandler=null;this.hashchangeHandler=null;}start(){this.currentUrl=window.location.href,this.emitNavigation(null,this.currentUrl,"initial"),this.originalPushState=history.pushState.bind(history),history.pushState=(...e)=>{this.originalPushState(...e),this.handleUrlChange("pushState");},this.originalReplaceState=history.replaceState.bind(history),history.replaceState=(...e)=>{this.originalReplaceState(...e),this.handleUrlChange("replaceState");},this.popstateHandler=()=>this.handleUrlChange("popstate"),window.addEventListener("popstate",this.popstateHandler),this.hashchangeHandler=()=>this.handleUrlChange("hashchange"),window.addEventListener("hashchange",this.hashchangeHandler);}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.hashchangeHandler&&(window.removeEventListener("hashchange",this.hashchangeHandler),this.hashchangeHandler=null);}handleUrlChange(e){let t=window.location.href;if(t!==this.currentUrl){let r=this.currentUrl;this.currentUrl=t,this.emitNavigation(r,t,e);}}emitNavigation(e,t,r){let s={from:e,to:t,trigger:r};this.emit({type:"navigation",timestamp:Date.now(),sessionId:this.sessionId,data:s});}};var K=["set-cookie"],S=class{constructor(e){this.options=e;}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,K)),r&&(t.requestHeaders&&r.denyHeaders.length>0&&(t.requestHeaders=this.filterHeaders(t.requestHeaders,r.denyHeaders)),t.responseHeaders&&r.denyHeaders.length>0&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,r.denyHeaders)),r.denyBodyUrls.length>0&&this.urlMatchesDeny(e.url,r.denyBodyUrls)&&(t.requestBody&&(t.requestBody="[REDACTED]"),t.responseBody&&(t.responseBody="[REDACTED]"))),t}filterHeaders(e,t){let r=t.map(i=>i.toLowerCase()),s={};for(let[i,a]of Object.entries(e))r.includes(i.toLowerCase())||(s[i]=a);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var C=class{constructor(e){this.config=e;this.buffer=[];this.bufferBytes=0;this.flushTimer=null;}start(){this.flushTimer=setInterval(()=>{this.flush();},this.config.flushIntervalMs);}stop(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush();}send(e){let t=JSON.stringify(e).length;this.buffer.push(e),this.bufferBytes+=t,this.bufferBytes>=this.config.flushMaxBytes&&this.flush();}flush(){if(this.buffer.length===0)return;let e=this.buffer;this.buffer=[],this.bufferBytes=0;let t={projectId:this.config.projectId,sessionId:this.config.sessionId,events:e,sentAt:Date.now()},r=JSON.stringify(t),s=`${this.config.apiUrl}/events`,i=r.length<6e4;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:i}).then(a=>{a.ok||console.warn(`[Ghostplay] Event batch rejected (${a.status})`);}).catch(()=>{this.sendBeacon(s,r);});}catch{this.sendBeacon(s,r);}}sendBeacon(e,t){typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(e,new Blob([t],{type:"application/json"}));}};var Q="0.1.0",o=null,l=null,d=null,D=null,R=null,T=null,H=null,x=null,B=null,N=null,M=null,u=null;function O(){if(!l)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){B&&n.type!=="rrweb"&&B.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),u?.send(n);}function ee(n){q(),o=_(n),l=new f(o.projectId);let e=l.start();M=new S({networkSanitize:o.networkSanitize}),u=new C({apiUrl:o.apiUrl,projectId:o.projectId,sessionId:e.id,flushIntervalMs:o.flushIntervalMs,flushMaxBytes:o.flushMaxBytes}),u.start(),B=new k,d=new g(e.id,c,{maskAllInputs:o.maskAllInputs,...o.maskInputSelector?{maskInputSelector:o.maskInputSelector}:{},...o.blockSelector?{blockSelector:o.blockSelector}:{},...o.ignoreSelector?{ignoreSelector:o.ignoreSelector}:{},...o.rrwebConfig});let t=()=>{d?.start(),setTimeout(()=>d?.takeFullSnapshot(),3e3);};document.readyState==="complete"?requestAnimationFrame(()=>requestAnimationFrame(t)):window.addEventListener("load",()=>requestAnimationFrame(t),{once:true}),D=new m(e.id,c),D.start(),o.recordNetwork&&(R=new y(e.id,r=>{let s=M.sanitizeNetworkEvent(r.data);c({...r,data:s});}),R.start()),o.recordConsole&&(T=new E(e.id,c),T.start()),o.trackPerformance&&(H=new w(e.id,c),H.start()),o.trackRageClicks&&(x=new b(e.id,c),x.start()),o.trackNavigation&&(N=new I(e.id,c),N.start());}function te(n,e){O(),l.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:l.current().id,data:{userId:n,traits:e}});}function re(n){O(),l.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:l.current().id,data:{tag:n}});}function ne(n){O(),l.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:l.current().id,data:n});}function q(){d?.stop(),D?.stop(),R?.stop(),T?.stop(),H?.stop(),x?.stop(),N?.stop(),u?.stop(),l?.end(),d=null,D=null,R=null,T=null,H=null,x=null,N=null,u=null,l=null,B=null,M=null,o=null;}var se={VERSION:Q,init:ee,identify:te,tag:re,setMetadata:ne,stop:q},Ne=se;
|
|
2
|
+
export{se as Ghostplay,Q as VERSION,Ne as default};//# sourceMappingURL=index.mjs.map
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|