@aj-2000-test/goodlogs-sdk 0.3.4 → 0.3.5
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.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/replay.cjs +1 -1
- package/dist/replay.d.cts +18 -28
- package/dist/replay.d.ts +18 -28
- package/dist/replay.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var I={us:"https://goodlogs-api-us.happyhill-a7c56143.centralindia.azurecontainerapps.io",eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function B(o){let t=o.split("_");return t.length>=4&&t[0]==="gl"?t[2]:"eu"}function y(o,t){if(t)return t;let e=B(o);return I[e]||I.eu}var G=5e3,F=50,v="0.3.
|
|
1
|
+
'use strict';var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var I={us:"https://goodlogs-api-us.happyhill-a7c56143.centralindia.azurecontainerapps.io",eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function B(o){let t=o.split("_");return t.length>=4&&t[0]==="gl"?t[2]:"eu"}function y(o,t){if(t)return t;let e=B(o);return I[e]||I.eu}var G=5e3,F=50,v="0.3.5",w="gl_anon_id",_="gl_session_id";function b(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{let t=Math.random()*16|0;return (o==="x"?t:t&3|8).toString(16)})}function M(){let o={$goodlogs_sdk:"js",$goodlogs_sdk_version:v};return typeof navigator>"u"||(typeof screen<"u"&&(o.$screen=`${screen.width}x${screen.height}`),typeof navigator<"u"&&(o.$language=navigator.language??""),typeof location<"u"&&(o.$url=location.href,o.$path=location.pathname,o.$page=location.pathname.split("/").filter(Boolean).pop()||"/"),typeof document<"u"&&(document.referrer&&(o.$referrer=document.referrer),document.title&&(o.$title=document.title))),o}function U(o,t){let e={$session_id:o,$distinct_id:t,$goodlogs_sdk:"js",$goodlogs_sdk_version:v};return typeof navigator>"u"||typeof location<"u"&&(e.$url=location.href,e.$path=location.pathname),e}function D(){if(!(typeof document<"u"))try{let t=new Error().stack?.split(`
|
|
2
2
|
`);if(!t)return;for(let e=3;e<Math.min(t.length,8);e++){let n=t[e]?.trim();if(!n||n.includes("goodlogs")&&(n.includes("client.")||n.includes("index.")))continue;let r=n.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):\d+\)?/);if(r){let s=r[1]||"<anonymous>",i=r[2]?.replace(/^.*[/\\]/,"")??"",d=r[3];return `${s}@${i}:${d}`}}}catch{}}function K(){if(typeof localStorage<"u"){let o=localStorage.getItem(w);if(o)return o;let t=b();return localStorage.setItem(w,t),t}return b()}function X(){if(typeof sessionStorage<"u"){let o=sessionStorage.getItem(_);if(o)return o;let t=b();return sessionStorage.setItem(_,t),t}return b()}function z(){let o="";for(let t=0;t<32;t++)o+=(Math.random()*16|0).toString(16);return o}function V(){let o="";for(let t=0;t<16;t++)o+=(Math.random()*16|0).toString(16);return o}function W(o,t){switch(o){case "lcp":return t<=2500?"good":t<=4e3?"needs-improvement":"poor";case "inp":return t<=200?"good":t<=500?"needs-improvement":"poor";case "cls":return t<=.1?"good":t<=.25?"needs-improvement":"poor";case "fcp":return t<=1800?"good":t<=3e3?"needs-improvement":"poor";case "ttfb":return t<=800?"good":t<=1800?"needs-improvement":"poor";default:return}}function J(o){if(!o)return [];let t=[];for(let e of o.split(`
|
|
3
3
|
`)){let n=e.trim();if(!n||n.startsWith("Error")||n.startsWith("at Error"))continue;let r=/^at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/.exec(n);if(r){let s=r[1]?.trim(),i=r[2];t.push({function:s&&s!=="<anonymous>"?s:void 0,filename:i,lineno:Number(r[3]),colno:Number(r[4]),abs_path:i,in_app:!i.includes("node_modules/")&&!i.includes("/dist/")&&!i.startsWith("internal/")});continue}if(r=/^([^@]*)@(.+?):(\d+):(\d+)$/.exec(n),r){let s=r[1]?.trim(),i=r[2];t.push({function:s||void 0,filename:i,lineno:Number(r[3]),colno:Number(r[4]),abs_path:i,in_app:!i.includes("node_modules/")&&!i.includes("/dist/")});}}return t}var R=class R{constructor(t){this.originalFetch=null;this.xhrPatched=false;this.logBuffer=[];this.eventBuffer=[];this.errorBuffer=[];this.vitalBuffer=[];this.spanBuffer=[];this.breadcrumbBuffer=[];this.timer=null;this.visibilityHandler=null;this.clickHandler=null;this.lastPath="";this.navTransaction=null;this.errorHandler=null;this.rejectionHandler=null;this.apiKey=t.apiKey,this.endpoint=y(t.apiKey,t.endpoint).replace(/\/+$/,""),this.flushInterval=t.flushInterval??G,this.batchSize=t.batchSize??F,this.defaultContext=t.defaultContext??{},this.onError=t.onError??(()=>{}),this.disabled=t.disabled??false,this.telemetry=t.telemetry??true,this.useEnvelope=t.useEnvelope??false,this.autoFetch=t.autoFetch??(this.useEnvelope&&t.autocapture!==false),this.anonymousId=K(),this.sessionId=X(),this.disabled&&typeof console<"u"&&console.warn("[GoodLogs] SDK is disabled. No events or logs will be sent."),this.disabled||(this.startTimer(),this.attachPageLifecycle(),t.autocapture!==false&&(this.attachClickCapture(),this.captureWebVitals(),this.attachPageviewCapture(),this.attachGlobalErrorHandlers()),this.autoFetch&&(this.attachFetchInstrumentation(),this.attachXhrInstrumentation()),this.sendTelemetry("init","info"));}log(t,e,n){if(this.disabled)return;let r=65536,s=e;e.length>r&&(s=e.slice(0,r-100)+`
|
|
4
4
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var I={us:"https://goodlogs-api-us.happyhill-a7c56143.centralindia.azurecontainerapps.io",eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function B(o){let t=o.split("_");return t.length>=4&&t[0]==="gl"?t[2]:"eu"}function y(o,t){if(t)return t;let e=B(o);return I[e]||I.eu}var G=5e3,F=50,v="0.3.
|
|
1
|
+
var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var I={us:"https://goodlogs-api-us.happyhill-a7c56143.centralindia.azurecontainerapps.io",eu:"https://goodlogs-api-eu.yellowmeadow-422296f6.japaneast.azurecontainerapps.io",ap:"https://goodlogs-api-ap.delightfulsand-90b72c09.southeastasia.azurecontainerapps.io"};function B(o){let t=o.split("_");return t.length>=4&&t[0]==="gl"?t[2]:"eu"}function y(o,t){if(t)return t;let e=B(o);return I[e]||I.eu}var G=5e3,F=50,v="0.3.5",w="gl_anon_id",_="gl_session_id";function b(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{let t=Math.random()*16|0;return (o==="x"?t:t&3|8).toString(16)})}function M(){let o={$goodlogs_sdk:"js",$goodlogs_sdk_version:v};return typeof navigator>"u"||(typeof screen<"u"&&(o.$screen=`${screen.width}x${screen.height}`),typeof navigator<"u"&&(o.$language=navigator.language??""),typeof location<"u"&&(o.$url=location.href,o.$path=location.pathname,o.$page=location.pathname.split("/").filter(Boolean).pop()||"/"),typeof document<"u"&&(document.referrer&&(o.$referrer=document.referrer),document.title&&(o.$title=document.title))),o}function U(o,t){let e={$session_id:o,$distinct_id:t,$goodlogs_sdk:"js",$goodlogs_sdk_version:v};return typeof navigator>"u"||typeof location<"u"&&(e.$url=location.href,e.$path=location.pathname),e}function D(){if(!(typeof document<"u"))try{let t=new Error().stack?.split(`
|
|
2
2
|
`);if(!t)return;for(let e=3;e<Math.min(t.length,8);e++){let n=t[e]?.trim();if(!n||n.includes("goodlogs")&&(n.includes("client.")||n.includes("index.")))continue;let r=n.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):\d+\)?/);if(r){let s=r[1]||"<anonymous>",i=r[2]?.replace(/^.*[/\\]/,"")??"",d=r[3];return `${s}@${i}:${d}`}}}catch{}}function K(){if(typeof localStorage<"u"){let o=localStorage.getItem(w);if(o)return o;let t=b();return localStorage.setItem(w,t),t}return b()}function X(){if(typeof sessionStorage<"u"){let o=sessionStorage.getItem(_);if(o)return o;let t=b();return sessionStorage.setItem(_,t),t}return b()}function z(){let o="";for(let t=0;t<32;t++)o+=(Math.random()*16|0).toString(16);return o}function V(){let o="";for(let t=0;t<16;t++)o+=(Math.random()*16|0).toString(16);return o}function W(o,t){switch(o){case "lcp":return t<=2500?"good":t<=4e3?"needs-improvement":"poor";case "inp":return t<=200?"good":t<=500?"needs-improvement":"poor";case "cls":return t<=.1?"good":t<=.25?"needs-improvement":"poor";case "fcp":return t<=1800?"good":t<=3e3?"needs-improvement":"poor";case "ttfb":return t<=800?"good":t<=1800?"needs-improvement":"poor";default:return}}function J(o){if(!o)return [];let t=[];for(let e of o.split(`
|
|
3
3
|
`)){let n=e.trim();if(!n||n.startsWith("Error")||n.startsWith("at Error"))continue;let r=/^at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/.exec(n);if(r){let s=r[1]?.trim(),i=r[2];t.push({function:s&&s!=="<anonymous>"?s:void 0,filename:i,lineno:Number(r[3]),colno:Number(r[4]),abs_path:i,in_app:!i.includes("node_modules/")&&!i.includes("/dist/")&&!i.startsWith("internal/")});continue}if(r=/^([^@]*)@(.+?):(\d+):(\d+)$/.exec(n),r){let s=r[1]?.trim(),i=r[2];t.push({function:s||void 0,filename:i,lineno:Number(r[3]),colno:Number(r[4]),abs_path:i,in_app:!i.includes("node_modules/")&&!i.includes("/dist/")});}}return t}var R=class R{constructor(t){this.originalFetch=null;this.xhrPatched=false;this.logBuffer=[];this.eventBuffer=[];this.errorBuffer=[];this.vitalBuffer=[];this.spanBuffer=[];this.breadcrumbBuffer=[];this.timer=null;this.visibilityHandler=null;this.clickHandler=null;this.lastPath="";this.navTransaction=null;this.errorHandler=null;this.rejectionHandler=null;this.apiKey=t.apiKey,this.endpoint=y(t.apiKey,t.endpoint).replace(/\/+$/,""),this.flushInterval=t.flushInterval??G,this.batchSize=t.batchSize??F,this.defaultContext=t.defaultContext??{},this.onError=t.onError??(()=>{}),this.disabled=t.disabled??false,this.telemetry=t.telemetry??true,this.useEnvelope=t.useEnvelope??false,this.autoFetch=t.autoFetch??(this.useEnvelope&&t.autocapture!==false),this.anonymousId=K(),this.sessionId=X(),this.disabled&&typeof console<"u"&&console.warn("[GoodLogs] SDK is disabled. No events or logs will be sent."),this.disabled||(this.startTimer(),this.attachPageLifecycle(),t.autocapture!==false&&(this.attachClickCapture(),this.captureWebVitals(),this.attachPageviewCapture(),this.attachGlobalErrorHandlers()),this.autoFetch&&(this.attachFetchInstrumentation(),this.attachXhrInstrumentation()),this.sendTelemetry("init","info"));}log(t,e,n){if(this.disabled)return;let r=65536,s=e;e.length>r&&(s=e.slice(0,r-100)+`
|
|
4
4
|
|
package/dist/replay.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var O=new Set([1,3,6]);function j(t){return t.every(e=>{if(e.type!==3)return false;let o=e.data?.source;return o!==void 0&&O.has(o)})}var w=null;async function V(t,e={}){if(typeof document>"u")return null;if(w)return w;let o=e.sampleRate??1;if(o<1&&Math.random()>o)return null;let i=e.rrwebRecord??await J();if(!i)return null;let r=t.endpoint,a=t.apiKey,p=t,C=e.flushIntervalMs??5e3,M=e.flushBytes??5e5,S=e.sessionTimeoutMs??1800*1e3,L=e.maxSessionMs??3600*1e3,s=p.sessionId,l=[],v=0,x=0,g=false,u=false,f=Date.now(),k=Date.now(),y=null,I=()=>{m(r,a,{session_id:s,distinct_id:p.getDistinctId(),start_url:typeof location<"u"?location.href:null,last_url:typeof location<"u"?location.href:null},e.onError);},R=()=>{d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError);},d=async(n=false)=>{if(l.length===0)return;if(!n&&j(l)){l=[],v=0;return}let c=l,U=c[0].timestamp,D=c[c.length-1].timestamp,G=c.length;l=[],v=0;let q=x++;try{let h=JSON.stringify(c),H=await K(h),N=`${r}/v1/replay/chunks?session_id=${encodeURIComponent(s)}&seq=${q}&start_ts=${encodeURIComponent(new Date(U).toISOString())}&end_ts=${encodeURIComponent(new Date(D).toISOString())}&event_count=${G}`;await fetch(N,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/gzip","Content-Encoding":"gzip"},body:H,keepalive:!0});}catch(h){e.onError?.(h);}m(r,a,{session_id:s,last_url:typeof location<"u"?location.href:null,last_activity_at:new Date(D).toISOString(),has_error:g||void 0},e.onError);},b=()=>{R();try{y?.();}catch(n){e.onError?.(n);}p.newSession(),s=p.getSessionId(),x=0,g=false,f=Date.now(),k=Date.now(),I(),_();},_=()=>{y=i({emit:n=>{if(u)return;n.type===3&&O.has(n.data?.source??-1)||(f=Date.now()),l.push(n),v+=256,v>=M&&d();},checkoutEveryNms:6e4,maskAllInputs:e.maskAllInputs??true,blockSelector:e.blockSelector??"[data-gl-block]",maskTextSelector:e.maskTextSelector??"[data-gl-mask]",sampling:{mousemove:50,mouseInteraction:true,scroll:150,input:"last"}});},A=setInterval(()=>{globalThis.__gl_replay_has_error__&&(g=true);},1e3),P=()=>{if(!u){u=true,clearInterval($),clearInterval(z),clearInterval(A),document?.removeEventListener("visibilitychange",E),typeof window<"u"&&window.removeEventListener("beforeunload",T);try{y?.();}catch(n){e.onError?.(n);}R(),w=null;}};I(),_();let $=setInterval(()=>{d();},C),z=setInterval(()=>{if(u)return;let n=Date.now();if(n-f>S){b();return}if(n-k>L){b();return}},1e4),E=()=>{u||(document?.visibilityState==="hidden"?(d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError)):document?.visibilityState==="visible"&&(Date.now()-f>S?b():f=Date.now()));};document.addEventListener("visibilitychange",E);let T=()=>{d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError);};typeof window<"u"&&window.addEventListener("beforeunload",T);let B={get sessionId(){return s},flush:()=>d(true),stop:P};return w=B,B}async function m(t,e,o,i){try{await fetch(`${t}/v1/replay/sessions`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(o),keepalive:!0});}catch(r){i?.(r);}}async function K(t){if(typeof CompressionStream>"u"||typeof Blob>"u"||typeof Response>"u")return t;let o=new Blob([t]).stream().pipeThrough(new CompressionStream("gzip")),i=await new Response(o).arrayBuffer();return new Blob([i],{type:"application/gzip"})}async function J(){try{let t=await import('rrweb');return t.record??t.default?.record??null}catch{return null}}exports.startReplay=V;
|
package/dist/replay.d.cts
CHANGED
|
@@ -3,17 +3,22 @@ import { G as GoodLogs } from './client-w3t1Yzjd.cjs';
|
|
|
3
3
|
/**
|
|
4
4
|
* Session Replay (rrweb) — optional, tree-split.
|
|
5
5
|
*
|
|
6
|
-
* Industry-standard
|
|
7
|
-
* 1. 30-min inactivity timeout → seals session,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 3.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* Industry-standard session lifecycle (PostHog/Sentry parity):
|
|
7
|
+
* 1. 30-min inactivity timeout → seals session, auto-starts a NEW
|
|
8
|
+
* session on next user activity (session rotation).
|
|
9
|
+
* 2. 60-min hard cap → same rotation behaviour.
|
|
10
|
+
* 3. Tab hide (visibilitychange=hidden) → flush + seal. Tab show →
|
|
11
|
+
* resume with a NEW session if timeout elapsed, else continue.
|
|
12
|
+
* 4. Idle suppression: flush with only mousemove/scroll/touch is dropped.
|
|
13
|
+
* 5. Dedup guard: calling startReplay() twice returns existing handle.
|
|
14
|
+
* 6. rrweb sampling: mousemove 50ms, scroll 150ms, input 'last'.
|
|
15
|
+
*
|
|
16
|
+
* Sessions are linked by distinct_id (user identity) so the dashboard
|
|
17
|
+
* can show all sessions for a user. Each session is a separate playable
|
|
18
|
+
* recording bounded to ≤60 min.
|
|
14
19
|
*
|
|
15
20
|
* rrweb is a real npm peer dep, dynamically imported so the core SDK
|
|
16
|
-
* bundle stays small
|
|
21
|
+
* bundle stays small.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
24
|
interface RrwebRecord {
|
|
@@ -21,11 +26,7 @@ interface RrwebRecord {
|
|
|
21
26
|
emit: (event: RrwebEvent, isCheckout?: boolean) => void;
|
|
22
27
|
checkoutEveryNms?: number;
|
|
23
28
|
maskAllInputs?: boolean;
|
|
24
|
-
maskInputOptions?: Record<string, boolean>;
|
|
25
|
-
blockClass?: string;
|
|
26
29
|
blockSelector?: string;
|
|
27
|
-
ignoreClass?: string;
|
|
28
|
-
maskTextClass?: string;
|
|
29
30
|
maskTextSelector?: string;
|
|
30
31
|
sampling?: Record<string, unknown>;
|
|
31
32
|
}): () => void;
|
|
@@ -36,36 +37,25 @@ interface RrwebEvent {
|
|
|
36
37
|
timestamp: number;
|
|
37
38
|
}
|
|
38
39
|
interface ReplayOptions {
|
|
39
|
-
/** Provide rrweb `record` yourself (bundled). Skips dynamic import. */
|
|
40
40
|
rrwebRecord?: RrwebRecord;
|
|
41
|
-
/** Flush interval ms (default 5000). */
|
|
42
41
|
flushIntervalMs?: number;
|
|
43
|
-
/** Flush when queue reaches this byte estimate (default 500_000). */
|
|
44
42
|
flushBytes?: number;
|
|
45
|
-
/** Sample rate 0..1 (default 1). */
|
|
46
43
|
sampleRate?: number;
|
|
47
|
-
/** Block elements matching this CSS selector. */
|
|
48
44
|
blockSelector?: string;
|
|
49
|
-
/** Mask text in elements matching this selector. */
|
|
50
45
|
maskTextSelector?: string;
|
|
51
|
-
/** Mask all inputs/textareas (default true). */
|
|
52
46
|
maskAllInputs?: boolean;
|
|
53
|
-
/** Inactivity timeout
|
|
47
|
+
/** Inactivity timeout before session rotation (default 30 min). */
|
|
54
48
|
sessionTimeoutMs?: number;
|
|
55
|
-
/**
|
|
49
|
+
/** Max session duration before rotation (default 60 min). */
|
|
56
50
|
maxSessionMs?: number;
|
|
57
|
-
/** Error callback. */
|
|
58
51
|
onError?: (e: unknown) => void;
|
|
59
52
|
}
|
|
60
53
|
interface ReplayHandle {
|
|
61
54
|
stop: () => void;
|
|
62
55
|
flush: () => Promise<void>;
|
|
63
|
-
|
|
56
|
+
/** Current session_id (changes on rotation). */
|
|
57
|
+
readonly sessionId: string;
|
|
64
58
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Start recording. Returns a handle with `stop()` and `flush()`.
|
|
67
|
-
* Second call is a no-op — returns the existing handle.
|
|
68
|
-
*/
|
|
69
59
|
declare function startReplay(gl: GoodLogs, options?: ReplayOptions): Promise<ReplayHandle | null>;
|
|
70
60
|
|
|
71
61
|
export { type ReplayHandle, type ReplayOptions, type RrwebEvent, startReplay };
|
package/dist/replay.d.ts
CHANGED
|
@@ -3,17 +3,22 @@ import { G as GoodLogs } from './client-w3t1Yzjd.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Session Replay (rrweb) — optional, tree-split.
|
|
5
5
|
*
|
|
6
|
-
* Industry-standard
|
|
7
|
-
* 1. 30-min inactivity timeout → seals session,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 3.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* Industry-standard session lifecycle (PostHog/Sentry parity):
|
|
7
|
+
* 1. 30-min inactivity timeout → seals session, auto-starts a NEW
|
|
8
|
+
* session on next user activity (session rotation).
|
|
9
|
+
* 2. 60-min hard cap → same rotation behaviour.
|
|
10
|
+
* 3. Tab hide (visibilitychange=hidden) → flush + seal. Tab show →
|
|
11
|
+
* resume with a NEW session if timeout elapsed, else continue.
|
|
12
|
+
* 4. Idle suppression: flush with only mousemove/scroll/touch is dropped.
|
|
13
|
+
* 5. Dedup guard: calling startReplay() twice returns existing handle.
|
|
14
|
+
* 6. rrweb sampling: mousemove 50ms, scroll 150ms, input 'last'.
|
|
15
|
+
*
|
|
16
|
+
* Sessions are linked by distinct_id (user identity) so the dashboard
|
|
17
|
+
* can show all sessions for a user. Each session is a separate playable
|
|
18
|
+
* recording bounded to ≤60 min.
|
|
14
19
|
*
|
|
15
20
|
* rrweb is a real npm peer dep, dynamically imported so the core SDK
|
|
16
|
-
* bundle stays small
|
|
21
|
+
* bundle stays small.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
24
|
interface RrwebRecord {
|
|
@@ -21,11 +26,7 @@ interface RrwebRecord {
|
|
|
21
26
|
emit: (event: RrwebEvent, isCheckout?: boolean) => void;
|
|
22
27
|
checkoutEveryNms?: number;
|
|
23
28
|
maskAllInputs?: boolean;
|
|
24
|
-
maskInputOptions?: Record<string, boolean>;
|
|
25
|
-
blockClass?: string;
|
|
26
29
|
blockSelector?: string;
|
|
27
|
-
ignoreClass?: string;
|
|
28
|
-
maskTextClass?: string;
|
|
29
30
|
maskTextSelector?: string;
|
|
30
31
|
sampling?: Record<string, unknown>;
|
|
31
32
|
}): () => void;
|
|
@@ -36,36 +37,25 @@ interface RrwebEvent {
|
|
|
36
37
|
timestamp: number;
|
|
37
38
|
}
|
|
38
39
|
interface ReplayOptions {
|
|
39
|
-
/** Provide rrweb `record` yourself (bundled). Skips dynamic import. */
|
|
40
40
|
rrwebRecord?: RrwebRecord;
|
|
41
|
-
/** Flush interval ms (default 5000). */
|
|
42
41
|
flushIntervalMs?: number;
|
|
43
|
-
/** Flush when queue reaches this byte estimate (default 500_000). */
|
|
44
42
|
flushBytes?: number;
|
|
45
|
-
/** Sample rate 0..1 (default 1). */
|
|
46
43
|
sampleRate?: number;
|
|
47
|
-
/** Block elements matching this CSS selector. */
|
|
48
44
|
blockSelector?: string;
|
|
49
|
-
/** Mask text in elements matching this selector. */
|
|
50
45
|
maskTextSelector?: string;
|
|
51
|
-
/** Mask all inputs/textareas (default true). */
|
|
52
46
|
maskAllInputs?: boolean;
|
|
53
|
-
/** Inactivity timeout
|
|
47
|
+
/** Inactivity timeout before session rotation (default 30 min). */
|
|
54
48
|
sessionTimeoutMs?: number;
|
|
55
|
-
/**
|
|
49
|
+
/** Max session duration before rotation (default 60 min). */
|
|
56
50
|
maxSessionMs?: number;
|
|
57
|
-
/** Error callback. */
|
|
58
51
|
onError?: (e: unknown) => void;
|
|
59
52
|
}
|
|
60
53
|
interface ReplayHandle {
|
|
61
54
|
stop: () => void;
|
|
62
55
|
flush: () => Promise<void>;
|
|
63
|
-
|
|
56
|
+
/** Current session_id (changes on rotation). */
|
|
57
|
+
readonly sessionId: string;
|
|
64
58
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Start recording. Returns a handle with `stop()` and `flush()`.
|
|
67
|
-
* Second call is a no-op — returns the existing handle.
|
|
68
|
-
*/
|
|
69
59
|
declare function startReplay(gl: GoodLogs, options?: ReplayOptions): Promise<ReplayHandle | null>;
|
|
70
60
|
|
|
71
61
|
export { type ReplayHandle, type ReplayOptions, type RrwebEvent, startReplay };
|
package/dist/replay.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var O=new Set([1,3,6]);function j(t){return t.every(e=>{if(e.type!==3)return false;let o=e.data?.source;return o!==void 0&&O.has(o)})}var w=null;async function V(t,e={}){if(typeof document>"u")return null;if(w)return w;let o=e.sampleRate??1;if(o<1&&Math.random()>o)return null;let i=e.rrwebRecord??await J();if(!i)return null;let r=t.endpoint,a=t.apiKey,p=t,C=e.flushIntervalMs??5e3,M=e.flushBytes??5e5,S=e.sessionTimeoutMs??1800*1e3,L=e.maxSessionMs??3600*1e3,s=p.sessionId,l=[],v=0,x=0,g=false,u=false,f=Date.now(),k=Date.now(),y=null,I=()=>{m(r,a,{session_id:s,distinct_id:p.getDistinctId(),start_url:typeof location<"u"?location.href:null,last_url:typeof location<"u"?location.href:null},e.onError);},R=()=>{d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError);},d=async(n=false)=>{if(l.length===0)return;if(!n&&j(l)){l=[],v=0;return}let c=l,U=c[0].timestamp,D=c[c.length-1].timestamp,G=c.length;l=[],v=0;let q=x++;try{let h=JSON.stringify(c),H=await K(h),N=`${r}/v1/replay/chunks?session_id=${encodeURIComponent(s)}&seq=${q}&start_ts=${encodeURIComponent(new Date(U).toISOString())}&end_ts=${encodeURIComponent(new Date(D).toISOString())}&event_count=${G}`;await fetch(N,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/gzip","Content-Encoding":"gzip"},body:H,keepalive:!0});}catch(h){e.onError?.(h);}m(r,a,{session_id:s,last_url:typeof location<"u"?location.href:null,last_activity_at:new Date(D).toISOString(),has_error:g||void 0},e.onError);},b=()=>{R();try{y?.();}catch(n){e.onError?.(n);}p.newSession(),s=p.getSessionId(),x=0,g=false,f=Date.now(),k=Date.now(),I(),_();},_=()=>{y=i({emit:n=>{if(u)return;n.type===3&&O.has(n.data?.source??-1)||(f=Date.now()),l.push(n),v+=256,v>=M&&d();},checkoutEveryNms:6e4,maskAllInputs:e.maskAllInputs??true,blockSelector:e.blockSelector??"[data-gl-block]",maskTextSelector:e.maskTextSelector??"[data-gl-mask]",sampling:{mousemove:50,mouseInteraction:true,scroll:150,input:"last"}});},A=setInterval(()=>{globalThis.__gl_replay_has_error__&&(g=true);},1e3),P=()=>{if(!u){u=true,clearInterval($),clearInterval(z),clearInterval(A),document?.removeEventListener("visibilitychange",E),typeof window<"u"&&window.removeEventListener("beforeunload",T);try{y?.();}catch(n){e.onError?.(n);}R(),w=null;}};I(),_();let $=setInterval(()=>{d();},C),z=setInterval(()=>{if(u)return;let n=Date.now();if(n-f>S){b();return}if(n-k>L){b();return}},1e4),E=()=>{u||(document?.visibilityState==="hidden"?(d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError)):document?.visibilityState==="visible"&&(Date.now()-f>S?b():f=Date.now()));};document.addEventListener("visibilitychange",E);let T=()=>{d(true),m(r,a,{session_id:s,ended_at:new Date().toISOString()},e.onError);};typeof window<"u"&&window.addEventListener("beforeunload",T);let B={get sessionId(){return s},flush:()=>d(true),stop:P};return w=B,B}async function m(t,e,o,i){try{await fetch(`${t}/v1/replay/sessions`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(o),keepalive:!0});}catch(r){i?.(r);}}async function K(t){if(typeof CompressionStream>"u"||typeof Blob>"u"||typeof Response>"u")return t;let o=new Blob([t]).stream().pipeThrough(new CompressionStream("gzip")),i=await new Response(o).arrayBuffer();return new Blob([i],{type:"application/gzip"})}async function J(){try{let t=await import('rrweb');return t.record??t.default?.record??null}catch{return null}}export{V as startReplay};
|