ghostplay-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var rrweb=require('rrweb');var P={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function B(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 {...P,...n}}function N(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var u=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:N(),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 h=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){this.stopFn=rrweb.record({...this.rrwebConfig,emit:e=>{this.emit({type:"rrweb",timestamp:e.timestamp,sessionId:this.sessionId,data:e});}})??null;}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var f=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,o)=>{let l={message:String(e),stack:o?.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:l}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,o):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 j=/ghostplay\.dev/,v=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,o=r?.method??"GET";if(j.test(s))return e.originalFetch.call(globalThis,t,r);let l=Date.now();try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let x={method:o.toUpperCase(),url:s,status:p.status,statusText:p.statusText,startTime:l,endTime:Date.now(),duration:Date.now()-l};e.emit({type:"network",timestamp:l,sessionId:e.sessionId,data:x});}return p}catch(p){if(e.active){let x={method:o.toUpperCase(),url:s,startTime:l,endTime:Date.now(),duration:Date.now()-l};e.emit({type:"network",timestamp:l,sessionId:e.sessionId,data:x});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};var M=["log","warn","error","info","debug"],m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of M)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 M)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 F={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},g=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]=F[e],o=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:o}});}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 y=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(o=>o.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(o=>r-o<1e3),s.timestamps.length>=3){let o={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:o}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(o=>o.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 E=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 A=["password"],_=["email","tel"],z=["set-cookie"],b=class{constructor(e){this.options=e;}shouldBlockElement(e){return this.options.blockSelector?e.matches(this.options.blockSelector):false}shouldIgnoreElement(e){return this.options.ignoreSelector?e.matches(this.options.ignoreSelector):false}shouldMaskInput(e){let t=e.type.toLowerCase();return !!(A.includes(t)||_.includes(t)||this.options.maskAllInputs||this.options.maskInputSelector&&e.matches(this.options.maskInputSelector))}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,z)),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(o=>o.toLowerCase()),s={};for(let[o,l]of Object.entries(e))r.includes(o.toLowerCase())||(s[o]=l);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var k=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`;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:!0}).catch(()=>{});}catch{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(s,new Blob([r],{type:"application/json"}));}}};var U="0.1.0",i=null,a=null,w=null,I=null,C=null,S=null,D=null,T=null,R=null,G=null,d=null;function H(){if(!a)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){R&&n.type!=="rrweb"&&R.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),d?.send(n);}function q(n){L(),i=B(n),a=new u(i.projectId);let e=a.start();G=new b({maskAllInputs:i.maskAllInputs,maskInputSelector:i.maskInputSelector,blockSelector:i.blockSelector,ignoreSelector:i.ignoreSelector,networkSanitize:i.networkSanitize}),d=new k({apiUrl:i.apiUrl,projectId:i.projectId,sessionId:e.id,flushIntervalMs:i.flushIntervalMs,flushMaxBytes:i.flushMaxBytes}),d.start(),R=new E,w=new h(e.id,c,i.rrwebConfig),w.start(),I=new f(e.id,c),I.start(),i.recordNetwork&&(C=new v(e.id,t=>{let r=G.sanitizeNetworkEvent(t.data);c({...t,data:r});}),C.start()),i.recordConsole&&(S=new m(e.id,c),S.start()),i.trackPerformance&&(D=new g(e.id,c),D.start()),i.trackRageClicks&&(T=new y(e.id,c),T.start());}function $(n,e){H(),a.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:a.current().id,data:{userId:n,traits:e}});}function W(n){H(),a.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:a.current().id,data:{tag:n}});}function Y(n){H(),a.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:a.current().id,data:n});}function L(){w?.stop(),I?.stop(),C?.stop(),S?.stop(),D?.stop(),T?.stop(),d?.stop(),a?.end(),w=null,I=null,C=null,S=null,D=null,T=null,d=null,a=null,R=null,G=null,i=null;}var J={VERSION:U,init:q,identify:$,tag:W,setMetadata:Y,stop:L},be=J;
2
+ exports.Ghostplay=J;exports.VERSION=U;exports.default=be;//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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/privacy.ts","../src/transport.ts","../src/index.ts"],"names":["DEFAULT_CONFIG","resolveConfig","config","generateId","timestamp","random","SessionManager","projectId","userId","traits","session","tag","data","Recorder","sessionId","emit","rrwebConfig","record","event","ErrorTracker","message","source","lineno","colno","error","reason","GHOSTPLAY_URL_PATTERN","NetworkInterceptor","self","input","init","url","method","startTime","response","LEVELS","ConsoleInterceptor","level","args","arg","THRESHOLDS","PerformanceTracker","o","metric","value","good","poor","rating","observer","list","entries","last","first","clsValue","entry","RageClickDetector","target","now","r","t","el","testId","className","Breadcrumbs","maxSize","category","crumb","ALWAYS_MASK_TYPES","DEFAULT_MASK_TYPES","ALWAYS_DENY_RESPONSE_HEADERS","PrivacyFilter","options","type","result","headers","denyList","denyLower","h","filtered","key","patterns","pattern","Transport","eventSize","events","batch","body","VERSION","sessionManager","recorder","errorTracker","networkInterceptor","consoleInterceptor","performanceTracker","rageClickDetector","breadcrumbs","privacyFilter","transport","ensureInitialized","handleEvent","userConfig","stop","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\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 | '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 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\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\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\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 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\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/;\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 try {\n const response = await self.originalFetch!.call(\n globalThis,\n input,\n init\n );\n\n if (self.active) {\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n status: response.status,\n statusText: response.statusText,\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 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","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 { GhostplayConfig, NetworkEventData } from './types';\n\nconst ALWAYS_MASK_TYPES = ['password'];\nconst DEFAULT_MASK_TYPES = ['email', 'tel'];\nconst ALWAYS_DENY_RESPONSE_HEADERS = ['set-cookie'];\n\ninterface PrivacyOptions {\n maskAllInputs?: boolean;\n maskInputSelector?: string;\n blockSelector?: string;\n ignoreSelector?: string;\n networkSanitize?: GhostplayConfig['networkSanitize'];\n}\n\nexport class PrivacyFilter {\n constructor(private options: PrivacyOptions) {}\n\n shouldBlockElement(el: Element): boolean {\n if (!this.options.blockSelector) return false;\n return el.matches(this.options.blockSelector);\n }\n\n shouldIgnoreElement(el: Element): boolean {\n if (!this.options.ignoreSelector) return false;\n return el.matches(this.options.ignoreSelector);\n }\n\n shouldMaskInput(el: HTMLInputElement): boolean {\n const type = el.type.toLowerCase();\n\n if (ALWAYS_MASK_TYPES.includes(type)) return true;\n if (DEFAULT_MASK_TYPES.includes(type)) return true;\n if (this.options.maskAllInputs) return true;\n if (\n this.options.maskInputSelector &&\n el.matches(this.options.maskInputSelector)\n ) {\n return true;\n }\n\n return false;\n }\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\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 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: true,\n }).catch(() => {});\n } catch {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }));\n }\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 { 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 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 maskAllInputs: config.maskAllInputs,\n maskInputSelector: config.maskInputSelector,\n blockSelector: config.blockSelector,\n ignoreSelector: config.ignoreSelector,\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, config.rrwebConfig);\n recorder.start();\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\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 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 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 ADDED
@@ -0,0 +1,3 @@
1
+ import {record}from'rrweb';var P={apiUrl:"https://api.ghostplay.dev/v1",maskAllInputs:true,recordNetwork:true,recordConsole:true,trackPerformance:true,trackRageClicks:true,flushIntervalMs:5e3,flushMaxBytes:5e4};function B(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 {...P,...n}}function N(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return `gs_${n}_${e}`}var u=class{constructor(e){this.projectId=e;this.session=null;}start(){return this.session={id:N(),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 h=class{constructor(e,t,r){this.sessionId=e;this.emit=t;this.rrwebConfig=r;this.stopFn=null;}start(){this.stopFn=record({...this.rrwebConfig,emit:e=>{this.emit({type:"rrweb",timestamp:e.timestamp,sessionId:this.sessionId,data:e});}})??null;}stop(){this.stopFn&&(this.stopFn(),this.stopFn=null);}};var f=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,o)=>{let l={message:String(e),stack:o?.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:l}),this.previousOnError?this.previousOnError.call(window,e,t,r,s,o):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 j=/ghostplay\.dev/,v=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,o=r?.method??"GET";if(j.test(s))return e.originalFetch.call(globalThis,t,r);let l=Date.now();try{let p=await e.originalFetch.call(globalThis,t,r);if(e.active){let x={method:o.toUpperCase(),url:s,status:p.status,statusText:p.statusText,startTime:l,endTime:Date.now(),duration:Date.now()-l};e.emit({type:"network",timestamp:l,sessionId:e.sessionId,data:x});}return p}catch(p){if(e.active){let x={method:o.toUpperCase(),url:s,startTime:l,endTime:Date.now(),duration:Date.now()-l};e.emit({type:"network",timestamp:l,sessionId:e.sessionId,data:x});}throw p}};}restoreFetch(){this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null);}};var M=["log","warn","error","info","debug"],m=class{constructor(e,t){this.sessionId=e;this.emit=t;this.originals={};}start(){for(let e of M)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 M)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 F={LCP:[1e3,4e3],FID:[100,300],CLS:[.1,.25],TTFB:[800,1800],INP:[200,500]},g=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]=F[e],o=t<=r?"good":t<=s?"needs-improvement":"poor";this.emit({type:"performance",timestamp:Date.now(),sessionId:this.sessionId,data:{metric:e,value:t,rating:o}});}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 y=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(o=>o.element===t);if(s||(s={element:t,timestamps:[]},this.recentClicks.push(s)),s.timestamps.push(r),s.timestamps=s.timestamps.filter(o=>r-o<1e3),s.timestamps.length>=3){let o={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:o}),s.timestamps=[];}this.recentClicks=this.recentClicks.filter(o=>o.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 E=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 A=["password"],_=["email","tel"],z=["set-cookie"],b=class{constructor(e){this.options=e;}shouldBlockElement(e){return this.options.blockSelector?e.matches(this.options.blockSelector):false}shouldIgnoreElement(e){return this.options.ignoreSelector?e.matches(this.options.ignoreSelector):false}shouldMaskInput(e){let t=e.type.toLowerCase();return !!(A.includes(t)||_.includes(t)||this.options.maskAllInputs||this.options.maskInputSelector&&e.matches(this.options.maskInputSelector))}sanitizeNetworkEvent(e){let t={...e},r=this.options.networkSanitize;return t.responseHeaders&&(t.responseHeaders=this.filterHeaders(t.responseHeaders,z)),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(o=>o.toLowerCase()),s={};for(let[o,l]of Object.entries(e))r.includes(o.toLowerCase())||(s[o]=l);return s}urlMatchesDeny(e,t){return t.some(r=>new RegExp("^"+r.replace(/\*/g,".*")+"$").test(e))}};var k=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`;try{fetch(s,{method:"POST",headers:{"Content-Type":"application/json","x-project-id":this.config.projectId},body:r,keepalive:!0}).catch(()=>{});}catch{typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon(s,new Blob([r],{type:"application/json"}));}}};var U="0.1.0",i=null,a=null,w=null,I=null,C=null,S=null,D=null,T=null,R=null,G=null,d=null;function H(){if(!a)throw new Error("[Ghostplay] SDK not initialized. Call Ghostplay.init() first.")}function c(n){R&&n.type!=="rrweb"&&R.add(n.type==="error"?"error":n.type==="network"?"network":n.type==="rage-click"?"click":"custom",`${n.type}: ${JSON.stringify(n.data).substring(0,100)}`),d?.send(n);}function q(n){L(),i=B(n),a=new u(i.projectId);let e=a.start();G=new b({maskAllInputs:i.maskAllInputs,maskInputSelector:i.maskInputSelector,blockSelector:i.blockSelector,ignoreSelector:i.ignoreSelector,networkSanitize:i.networkSanitize}),d=new k({apiUrl:i.apiUrl,projectId:i.projectId,sessionId:e.id,flushIntervalMs:i.flushIntervalMs,flushMaxBytes:i.flushMaxBytes}),d.start(),R=new E,w=new h(e.id,c,i.rrwebConfig),w.start(),I=new f(e.id,c),I.start(),i.recordNetwork&&(C=new v(e.id,t=>{let r=G.sanitizeNetworkEvent(t.data);c({...t,data:r});}),C.start()),i.recordConsole&&(S=new m(e.id,c),S.start()),i.trackPerformance&&(D=new g(e.id,c),D.start()),i.trackRageClicks&&(T=new y(e.id,c),T.start());}function $(n,e){H(),a.identify(n,e),c({type:"identify",timestamp:Date.now(),sessionId:a.current().id,data:{userId:n,traits:e}});}function W(n){H(),a.addTag(n),c({type:"tag",timestamp:Date.now(),sessionId:a.current().id,data:{tag:n}});}function Y(n){H(),a.setMetadata(n),c({type:"metadata",timestamp:Date.now(),sessionId:a.current().id,data:n});}function L(){w?.stop(),I?.stop(),C?.stop(),S?.stop(),D?.stop(),T?.stop(),d?.stop(),a?.end(),w=null,I=null,C=null,S=null,D=null,T=null,d=null,a=null,R=null,G=null,i=null;}var J={VERSION:U,init:q,identify:$,tag:W,setMetadata:Y,stop:L},be=J;
2
+ export{J as Ghostplay,U as VERSION,be as default};//# sourceMappingURL=index.mjs.map
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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/privacy.ts","../src/transport.ts","../src/index.ts"],"names":["DEFAULT_CONFIG","resolveConfig","config","generateId","timestamp","random","SessionManager","projectId","userId","traits","session","tag","data","Recorder","sessionId","emit","rrwebConfig","record","event","ErrorTracker","message","source","lineno","colno","error","reason","GHOSTPLAY_URL_PATTERN","NetworkInterceptor","self","input","init","url","method","startTime","response","LEVELS","ConsoleInterceptor","level","args","arg","THRESHOLDS","PerformanceTracker","o","metric","value","good","poor","rating","observer","list","entries","last","first","clsValue","entry","RageClickDetector","target","now","r","t","el","testId","className","Breadcrumbs","maxSize","category","crumb","ALWAYS_MASK_TYPES","DEFAULT_MASK_TYPES","ALWAYS_DENY_RESPONSE_HEADERS","PrivacyFilter","options","type","result","headers","denyList","denyLower","h","filtered","key","patterns","pattern","Transport","eventSize","events","batch","body","VERSION","sessionManager","recorder","errorTracker","networkInterceptor","consoleInterceptor","performanceTracker","rageClickDetector","breadcrumbs","privacyFilter","transport","ensureInitialized","handleEvent","userConfig","stop","sanitized","identify","tagName","setMetadata","Ghostplay","index_default"],"mappings":"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","file":"index.mjs","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\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 | '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 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\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\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\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 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\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/;\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 try {\n const response = await self.originalFetch!.call(\n globalThis,\n input,\n init\n );\n\n if (self.active) {\n const data: NetworkEventData = {\n method: method.toUpperCase(),\n url,\n status: response.status,\n statusText: response.statusText,\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 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","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 { GhostplayConfig, NetworkEventData } from './types';\n\nconst ALWAYS_MASK_TYPES = ['password'];\nconst DEFAULT_MASK_TYPES = ['email', 'tel'];\nconst ALWAYS_DENY_RESPONSE_HEADERS = ['set-cookie'];\n\ninterface PrivacyOptions {\n maskAllInputs?: boolean;\n maskInputSelector?: string;\n blockSelector?: string;\n ignoreSelector?: string;\n networkSanitize?: GhostplayConfig['networkSanitize'];\n}\n\nexport class PrivacyFilter {\n constructor(private options: PrivacyOptions) {}\n\n shouldBlockElement(el: Element): boolean {\n if (!this.options.blockSelector) return false;\n return el.matches(this.options.blockSelector);\n }\n\n shouldIgnoreElement(el: Element): boolean {\n if (!this.options.ignoreSelector) return false;\n return el.matches(this.options.ignoreSelector);\n }\n\n shouldMaskInput(el: HTMLInputElement): boolean {\n const type = el.type.toLowerCase();\n\n if (ALWAYS_MASK_TYPES.includes(type)) return true;\n if (DEFAULT_MASK_TYPES.includes(type)) return true;\n if (this.options.maskAllInputs) return true;\n if (\n this.options.maskInputSelector &&\n el.matches(this.options.maskInputSelector)\n ) {\n return true;\n }\n\n return false;\n }\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\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 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: true,\n }).catch(() => {});\n } catch {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }));\n }\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 { 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 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 maskAllInputs: config.maskAllInputs,\n maskInputSelector: config.maskInputSelector,\n blockSelector: config.blockSelector,\n ignoreSelector: config.ignoreSelector,\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, config.rrwebConfig);\n recorder.start();\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\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 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 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/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "ghostplay-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Ghostplay - Session replay, error tracking, and automatic Playwright test generation",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/gufao/ghostplay-sdk"
19
+ },
20
+ "author": "ThreadCode",
21
+ "homepage": "https://github.com/gufao/ghostplay-sdk#readme",
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "keywords": ["session-replay", "error-tracking", "monitoring", "playwright", "rrweb"],
29
+ "license": "MIT",
30
+ "packageManager": "pnpm@10.30.0",
31
+ "dependencies": {
32
+ "rrweb": "2.0.0-alpha.4"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.5.2",
36
+ "jsdom": "^29.0.2",
37
+ "tsup": "^8.5.1",
38
+ "typescript": "^6.0.2",
39
+ "vitest": "^4.1.3"
40
+ }
41
+ }