@faststats/web 0.1.1 → 0.1.3
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/CHANGELOG.md +8 -0
- package/dist/analytics-p8pLmlA-.js +1 -0
- package/dist/error-CMM8PYcW.js +3 -0
- package/dist/module.d.ts +11 -13
- package/dist/module.js +1 -1
- package/dist/replay-DeRZXYG3.js +1 -0
- package/dist/types-B5zbhOWK.js +1 -0
- package/dist/web-vitals-DU_i1Ejx.js +1 -0
- package/package.json +1 -1
- package/src/analytics.ts +4 -13
- package/src/error.ts +13 -17
- package/src/replay.ts +25 -24
- package/src/utils/identifiers.ts +8 -1
- package/src/web-vitals.ts +2 -4
- package/dist/error-DtTBFJh_.js +0 -3
- package/dist/replay-ZmimkIBT.js +0 -1
- package/dist/types-E07i1PxI.js +0 -1
- package/dist/web-vitals-CUXDm9A3.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./types-B5zbhOWK.js";let c=null;function l(){return c}function u(e,t){typeof window>`u`||g()||c?.track(e,t??{})}function d(e,t,n){typeof window>`u`||g()||c?.identify(e,t,n??{})}function f(e=!0){typeof window>`u`||g()||c?.logout(e)}function p(e){c?.setConsentMode(e)}function m(){p(`granted`)}function h(){p(`denied`)}function g(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function _(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`}=e,s=n instanceof Blob?n:new Blob([n],{type:r});if(navigator.sendBeacon?.(t,s))return a&&console.log(`${o} ✓ Sent via beacon`),!0;try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:!0}),s=e.ok;return a&&(s?console.log(`${o} ✓ Sent via fetch`):console.warn(`${o} ✗ Failed: ${e.status}`)),s}catch{return a&&console.warn(`${o} ✗ Failed to send`),!1}}function v(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function y(){let e={};if(!location.search)return e;let t=new URLSearchParams(location.search);for(let n of[`utm_source`,`utm_medium`,`utm_campaign`,`utm_term`,`utm_content`]){let r=t.get(n);r&&(e[n]=r)}return e}var b=class{endpoint;debug;started=!1;pageKey=``;navTimer=null;heartbeatTimer=null;scrollDepth=0;pageEntryTime=0;pagePath=``;pageUrl=``;pageHash=``;hasLeftCurrentPage=!1;scrollHandler=null;consentMode;cookielessWhilePending;constructor(e){this.options=e,this.endpoint=e.endpoint??`https://metrics.faststats.dev/v1/web`,this.debug=e.debug??!1,this.consentMode=e.consent?.mode??`granted`,this.cookielessWhilePending=e.consent?.cookielessWhilePending??!0,(e.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(g()){this.log(`disabled`);return}c=this,setTimeout(()=>void this.start(),0)}}async start(){if(this.started||typeof window>`u`)return;if(c&&c!==this){this.log(`already started by another instance`);return}if(g()){this.log(`disabled`);return}this.started=!0,c=this,t(this.isCookielessMode());let e=this.options;if(e.errorTracking?.enabled??e.trackErrors){let{default:t}=await import(`./error-CMM8PYcW.js`);new t({siteKey:e.siteKey,endpoint:this.endpoint,debug:this.debug}).start(),this.log(`error loaded`)}if(e.trackWebVitals){let{default:t}=await import(`./web-vitals-DU_i1Ejx.js`);new t({siteKey:e.siteKey,endpoint:this.endpoint,debug:this.debug,samplingPercentage:s(e.webVitals?.sampling?.percentage)}).start(),this.log(`web-vitals loaded`)}if(e.trackReplay){let{default:t}=await import(`./replay-DeRZXYG3.js`);new t({siteKey:e.siteKey,endpoint:this.endpoint,debug:this.debug,...e.replayOptions}).start(),this.log(`replay loaded`)}this.enterPage(),this.pageview({trigger:`load`}),this.links(),this.trackScroll(),this.startHeartbeat(),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`?this.leavePage():this.startHeartbeat()}),window.addEventListener(`pagehide`,()=>this.leavePage()),window.addEventListener(`popstate`,()=>this.navigate()),e.trackHash&&window.addEventListener(`hashchange`,()=>this.navigate()),this.patch()}pageview(e={}){let t=`${location.pathname}|${this.options.trackHash??!1?location.hash:``}`;t!==this.pageKey&&(this.pageKey=t,this.send(`pageview`,e))}track(e,t={}){this.send(e,t)}identify(e,t,n={}){if(g()||this.isCookielessMode())return;let i=e.trim(),a=t.trim();!i||!a||_({url:this.endpoint.replace(/\/v1\/web$/,`/v1/identify`),data:JSON.stringify({token:this.options.siteKey,identifier:r(!1),externalId:i,email:a,name:n.name?.trim()||void 0,phone:n.phone?.trim()||void 0,avatarUrl:n.avatarUrl?.trim()||void 0,traits:n.traits??{}}),contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] identify`})}logout(e=!0){g()||(e&&i(this.isCookielessMode()),o())}setConsentMode(e){this.consentMode=e,t(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return r(this.isCookielessMode())}getSessionId(){return a()}isCookielessMode(){return this.options.cookieless||this.consentMode===`denied`?!0:this.consentMode===`pending`?this.cookielessWhilePending:!1}send(e,t={}){let n=r(this.isCookielessMode()),i=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:a(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...y(),...t}});this.log(e),_({url:this.endpoint,data:i,contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] ${e}`})}enterPage(){this.pageEntryTime=Date.now(),this.pagePath=location.pathname,this.pageUrl=location.href,this.pageHash=location.hash,this.scrollDepth=0,this.hasLeftCurrentPage=!1}leavePage(){if(this.hasLeftCurrentPage)return;this.hasLeftCurrentPage=!0;let e=Date.now();this.send(`page_leave`,{page:this.pagePath,url:this.pageUrl,time_on_page:e-this.pageEntryTime,scroll_depth:this.scrollDepth,session_duration:e-n()})}trackScroll(){this.scrollHandler&&window.removeEventListener(`scroll`,this.scrollHandler);let e=()=>{let e=document.documentElement,t=document.body,n=window.innerHeight,r=Math.max(e.scrollHeight,t.scrollHeight);if(r<=n){this.scrollDepth=100;return}let i=Math.min(100,Math.round(((window.scrollY||e.scrollTop)+n)/r*100));i>this.scrollDepth&&(this.scrollDepth=i)};this.scrollHandler=e,e(),window.addEventListener(`scroll`,e,{passive:!0})}startHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=setInterval(()=>{if(document.visibilityState===`hidden`){clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;return}e()},300*1e3)}navigate(){this.navTimer&&clearTimeout(this.navTimer),this.navTimer=setTimeout(()=>{this.navTimer=null;let e=location.pathname!==this.pagePath,t=(this.options.trackHash??!1)&&location.hash!==this.pageHash;!e&&!t||(this.leavePage(),this.enterPage(),this.trackScroll(),this.pageview({trigger:`navigation`}))},300)}patch(){let e=()=>this.navigate();for(let t of[`pushState`,`replaceState`]){let n=history[t];history[t]=function(...t){let r=n.apply(this,t);return e(),r}}}links(){let e=e=>{let t=v(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};document.addEventListener(`click`,e),document.addEventListener(`auxclick`,e)}};export{f as a,_ as c,g as i,p as l,l as n,m as o,d as r,h as s,b as t,u};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{n as e,r as t}from"./types-B5zbhOWK.js";import{c as n}from"./analytics-p8pLmlA-.js";var r=class{endpoint;siteKey;debug;flushInterval;maxQueueSize;handledErrors=new WeakSet;errorCounts=new Map;flushTimer=null;started=!1;constructor(e){this.siteKey=e.siteKey,this.endpoint=e.endpoint??`https://metrics.faststats.dev/v1/web`,this.debug=e.debug??!1,this.flushInterval=e.flushInterval??5e3,this.maxQueueSize=e.maxQueueSize??50}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.handleErrorEvent),window.addEventListener(`unhandledrejection`,this.handleRejection),this.flushTimer=setInterval(()=>this.flush(),this.flushInterval),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()}),window.addEventListener(`pagehide`,()=>this.flush()),this.debug&&console.log(`[ErrorTracker] Started listening for errors`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.handleErrorEvent),window.removeEventListener(`unhandledrejection`,this.handleRejection),this.flushTimer&&=(clearInterval(this.flushTimer),null),this.flush(),this.debug&&console.log(`[ErrorTracker] Stopped listening for errors`))}handleErrorEvent=e=>{let t=e.error;if(t instanceof Error){if(this.handledErrors.has(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate error:`,t.message);return}this.handledErrors.add(t)}this.queueError({message:e.message||(t?.message??`Unknown error`),filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:t?.stack||void 0,type:`error`})};handleRejection=e=>{let t=e.reason;if(t instanceof Error){if(this.handledErrors.has(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate rejection:`,t.message);return}this.handledErrors.add(t)}this.queueError({message:t instanceof Error?t.message:typeof t==`string`?t:`Unhandled promise rejection`,stack:t instanceof Error?t.stack:void 0,type:`unhandledrejection`})};isExtensionError(e){return!!(e.filename?.startsWith(`chrome-extension://`)||e.stack&&e.stack.split(`
|
|
2
|
+
`).find(e=>e.trim().startsWith(`at `))?.includes(`chrome-extension://`))}parseStack(e){if(e)return e.split(`
|
|
3
|
+
`).map(e=>e.trim()).filter(e=>e.length>0)}async generateErrorHash(e){let t=[e.type,e.message,e.filename??``,e.lineno??``].join(`:`),n=new TextEncoder().encode(t),r=await crypto.subtle.digest(`SHA-256`,n);return`err_${Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}`}async queueError(e){if(this.isExtensionError(e))return;this.debug&&console.log(`[ErrorTracker] Captured error:`,e);let t=await this.generateErrorHash(e),n=this.errorCounts.get(t);if(n)n.count++,this.debug&&console.log(`[ErrorTracker] Incremented count for ${t} to ${n.count}`);else{let n={error:e.type===`unhandledrejection`?`UnhandledRejection`:`Error`,message:e.message,stack:this.parseStack(e.stack)};this.errorCounts.set(t,{entry:n,hash:t,count:1}),this.debug&&console.log(`[ErrorTracker] Queued new error: ${t}`)}this.errorCounts.size>=this.maxQueueSize&&this.flush()}captureError(e){if(this.handledErrors.has(e)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate manual capture:`,e.message);return}this.handledErrors.add(e),this.queueError({message:e.message,stack:e.stack,type:`error`})}flush(){if(this.errorCounts.size===0)return;let r=[];for(let{entry:e,hash:t,count:n}of this.errorCounts.values())r.push({...e,hash:t,count:n});this.errorCounts.clear(),this.debug&&console.log(`[ErrorTracker] Flushing errors:`,r);let i=e(),a=typeof document<`u`?{url:location.href,page:location.pathname,referrer:document.referrer||null}:{url:``,page:``,referrer:null},o=globalThis.__SOURCEMAPS_BUILD__,s=typeof o?.buildId==`string`&&o.buildId.trim().length>0?o.buildId:void 0,c={token:this.siteKey,...i?{userId:i}:{},sessionId:t(),...s?{buildId:s}:{},data:{url:a.url,page:a.page,referrer:a.referrer,title:typeof document<`u`?document.title:``},errors:r},l=JSON.stringify(c);this.debug&&console.log(`[ErrorTracker] Payload:`,l),n({url:this.endpoint,data:l,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{r as default};
|
package/dist/module.d.ts
CHANGED
|
@@ -352,16 +352,6 @@ declare global {
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
//#endregion
|
|
355
|
-
//#region src/utils/types.d.ts
|
|
356
|
-
interface SendDataOptions {
|
|
357
|
-
url: string;
|
|
358
|
-
data: string | Blob;
|
|
359
|
-
contentType?: string;
|
|
360
|
-
headers?: Record<string, string>;
|
|
361
|
-
debug?: boolean;
|
|
362
|
-
debugPrefix?: string;
|
|
363
|
-
}
|
|
364
|
-
//#endregion
|
|
365
355
|
//#region src/replay.d.ts
|
|
366
356
|
interface ReplayTrackerOptions {
|
|
367
357
|
siteKey: string;
|
|
@@ -370,6 +360,7 @@ interface ReplayTrackerOptions {
|
|
|
370
360
|
samplingPercentage?: number;
|
|
371
361
|
flushInterval?: number;
|
|
372
362
|
maxEvents?: number;
|
|
363
|
+
minReplayLengthMs?: number;
|
|
373
364
|
sampling?: recordOptions<eventWithTime>["sampling"];
|
|
374
365
|
slimDOMOptions?: SlimDOMOptions;
|
|
375
366
|
maskAllInputs?: boolean;
|
|
@@ -381,9 +372,16 @@ interface ReplayTrackerOptions {
|
|
|
381
372
|
checkoutEveryNms?: number;
|
|
382
373
|
checkoutEveryNth?: number;
|
|
383
374
|
recordConsole?: boolean;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/utils/types.d.ts
|
|
378
|
+
interface SendDataOptions {
|
|
379
|
+
url: string;
|
|
380
|
+
data: string | Blob;
|
|
381
|
+
contentType?: string;
|
|
382
|
+
headers?: Record<string, string>;
|
|
383
|
+
debug?: boolean;
|
|
384
|
+
debugPrefix?: string;
|
|
387
385
|
}
|
|
388
386
|
//#endregion
|
|
389
387
|
//#region src/analytics.d.ts
|
package/dist/module.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import"./types-B5zbhOWK.js";import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c,u as l}from"./analytics-p8pLmlA-.js";export{c as WebAnalytics,i as getInstance,o as identify,n as isTrackingDisabled,e as logout,a as optIn,s as optOut,t as sendData,r as setConsentMode,l as trackEvent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t,t as n}from"./types-B5zbhOWK.js";import{c as r}from"./analytics-p8pLmlA-.js";import{getRecordConsolePlugin as i}from"@rrweb/rrweb-plugin-console-record";import{record as a}from"rrweb";var o=class{endpoint;siteKey;debug;flushInterval;maxEvents;sampling;slimDOMOptions;maskAllInputs;maskInputOptions;blockClass;blockSelector;maskTextClass;maskTextSelector;checkoutEveryNms;checkoutEveryNth;samplingPercentage;recordConsole;events=[];flushTimer=null;stopRecording=void 0;started=!1;startTime=0;minLengthFlushScheduled=!1;sequenceNumber=0;pendingBatches=[];isFlushing=!1;compressionSupported=!1;sessionSamplingSeed;minReplayLengthMs;constructor(e){this.siteKey=e.siteKey,this.endpoint=e.endpoint?.replace(/\/v1\/web$/,`/v1/replay`)??`https://metrics.faststats.dev/v1/replay`,this.debug=e.debug??!1,this.samplingPercentage=n(e.samplingPercentage),this.flushInterval=e.flushInterval??1e4,this.maxEvents=e.maxEvents??500,this.sampling=e.sampling??{mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},this.slimDOMOptions=e.slimDOMOptions??{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0},this.maskAllInputs=e.maskAllInputs??!0,this.maskInputOptions=e.maskInputOptions??{password:!0,email:!0,tel:!0},this.blockClass=e.blockClass,this.blockSelector=e.blockSelector,this.maskTextClass=e.maskTextClass,this.maskTextSelector=e.maskTextSelector,this.checkoutEveryNms=e.checkoutEveryNms??6e4,this.checkoutEveryNth=e.checkoutEveryNth,this.recordConsole=e.recordConsole??!0,this.minReplayLengthMs=e.minReplayLengthMs??3e3,this.sessionSamplingSeed=Math.random()*100,typeof window<`u`&&(this.compressionSupported=`CompressionStream`in window)}start(){if(this.started||typeof window>`u`||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;this.started=!0,this.startTime=Date.now(),this.debug&&console.log(`[Replay] Recording started`);let e={emit:(e,t)=>this.handleEvent(e,t),sampling:this.sampling,slimDOMOptions:this.slimDOMOptions,maskAllInputs:this.maskAllInputs,checkoutEveryNms:this.checkoutEveryNms};this.maskInputOptions&&(e.maskInputOptions=this.maskInputOptions),this.blockClass&&(e.blockClass=this.blockClass),this.blockSelector&&(e.blockSelector=this.blockSelector),this.maskTextClass&&(e.maskTextClass=this.maskTextClass),this.maskTextSelector&&(e.maskTextSelector=this.maskTextSelector),this.checkoutEveryNth&&(e.checkoutEveryNth=this.checkoutEveryNth),this.recordConsole&&(e.plugins=[i()]),this.stopRecording=a(e),this.flushTimer=setInterval(()=>{this.scheduleFlush()},this.flushInterval),window.addEventListener(`beforeunload`,this.handleUnload),window.addEventListener(`pagehide`,this.handleUnload),document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}stop(){if(this.started){if(this.started=!1,this.debug&&console.log(`[Replay] Recording stopped`),this.stopRecording?.(),this.stopRecording=void 0,this.flushTimer&&=(clearInterval(this.flushTimer),null),window.removeEventListener(`beforeunload`,this.handleUnload),window.removeEventListener(`pagehide`,this.handleUnload),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),!this.hasReachedMinLength()){this.events=[],this.debug&&console.log(`[Replay] Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush()}}handleEvent(e,t){this.events.push(e),t||this.events.length>=this.maxEvents?this.scheduleFlush():!this.minLengthFlushScheduled&&this.hasReachedMinLength()&&(this.minLengthFlushScheduled=!0,this.scheduleFlush())}hasReachedMinLength(){return this.minReplayLengthMs<=0?!0:Date.now()-this.startTime>=this.minReplayLengthMs}scheduleFlush(){this.isFlushing||this.events.length===0||(typeof window<`u`&&`requestIdleCallback`in window?window.requestIdleCallback(()=>this.flush(),{timeout:2e3}):setTimeout(()=>this.flush(),0))}handleUnload=()=>{this.flush()};handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.flushTimer&&=(clearInterval(this.flushTimer),null),this.flush()):document.visibilityState===`visible`&&this.started&&(this.flushTimer||=setInterval(()=>{this.scheduleFlush()},this.flushInterval))};async flush(){if(this.events.length===0){this.processPendingBatches();return}if(!this.hasReachedMinLength()){this.debug&&console.log(`[Replay] Too short (${Date.now()-this.startTime}ms), skipping`),this.processPendingBatches();return}if(this.isFlushing)return;this.isFlushing=!0;let n=this.events;this.events=[];let r=e(),i={token:this.siteKey,sessionId:t(),...r?{identifier:r}:{},sequence:this.sequenceNumber++,timestamp:Date.now(),url:window.location.href,events:n};this.debug&&console.log(`[Replay] Sending ${n.length} events (seq: ${i.sequence})`);try{let e,t=!1;if(this.compressionSupported)try{e=await this.compress(JSON.stringify(i)),t=!0}catch{this.debug&&console.warn(`[Replay] Compression failed, using uncompressed`),e=new Blob([JSON.stringify(i)],{type:`application/json`})}else e=new Blob([JSON.stringify(i)],{type:`application/json`});await this.send(e,t)||this.pendingBatches.push({batch:i,isCompressed:t,retries:0})}catch(e){this.debug&&console.warn(`[Replay] Flush error:`,e),this.pendingBatches.push({batch:i,isCompressed:!1,retries:0})}finally{this.isFlushing=!1,this.processPendingBatches()}}async processPendingBatches(){if(this.pendingBatches.length===0)return;let e=this.pendingBatches.shift();if(e){if(e.retries>=3){this.debug&&console.warn(`[Replay] Max retries reached, dropping batch ${e.batch.sequence}`),this.processPendingBatches();return}e.retries++;try{let t;if(e.isCompressed&&this.compressionSupported)try{t=await this.compress(JSON.stringify(e.batch))}catch{t=new Blob([JSON.stringify(e.batch)],{type:`application/json`}),e.isCompressed=!1}else t=new Blob([JSON.stringify(e.batch)],{type:`application/json`});await this.send(t,e.isCompressed)?this.processPendingBatches():(this.pendingBatches.push(e),setTimeout(()=>this.processPendingBatches(),1e3*e.retries))}catch{this.debug&&console.warn(`[Replay] Retry ${e.retries} failed`),this.pendingBatches.push(e),setTimeout(()=>this.processPendingBatches(),1e3*e.retries)}}}async compress(e){if(!this.compressionSupported)throw Error(`Compression not supported`);let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();r.write(t),r.close();let i=[],a=n.readable.getReader();for(;;){let{done:e,value:t}=await a.read();if(e)break;t&&i.push(t)}let o=i.reduce((e,t)=>e+t.length,0),s=new Uint8Array(o),c=0;for(let e of i)s.set(e,c),c+=e.length;if(this.debug){let e=(s.length/t.length*100).toFixed(1);console.log(`[Replay] Compressed: ${t.length} → ${s.length} bytes (${e}%)`)}return new Blob([s],{type:`application/octet-stream`})}async send(e,t){let n=t?`${this.endpoint}?encoding=gzip`:this.endpoint,i=(e.size/1024).toFixed(1);return r({url:n,data:e,contentType:t?`application/octet-stream`:`application/json`,headers:t?{"Content-Encoding":`gzip`}:void 0,debug:this.debug,debugPrefix:`[Replay] ${i}KB`})??Promise.resolve(!1)}getSessionId(){return t()}};export{o as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=!1;function t(t){e=t}function n(t){return t??e?``:r()}function r(){if(typeof localStorage>`u`)return``;let e=localStorage.getItem(`faststats_anon_id`);if(e)return e;let t=crypto.randomUUID();return localStorage.setItem(`faststats_anon_id`,t),t}function i(e){return e||typeof localStorage>`u`?``:(localStorage.removeItem(`faststats_anon_id`),r())}function a(){if(typeof sessionStorage>`u`)return``;let e=sessionStorage.getItem(`session_id`),t=sessionStorage.getItem(`session_timestamp`);if(e&&t){if(Date.now()-Number.parseInt(t,10)<18e5)return sessionStorage.setItem(`session_timestamp`,Date.now().toString()),e;sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`)}let n=Date.now().toString(),r=crypto.randomUUID();return sessionStorage.setItem(`session_id`,r),sessionStorage.setItem(`session_timestamp`,n),sessionStorage.setItem(`session_start`,n),r}function o(){return typeof sessionStorage>`u`?``:(sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`),a())}function s(){typeof sessionStorage>`u`||sessionStorage.getItem(`session_id`)&&sessionStorage.setItem(`session_timestamp`,Date.now().toString())}function c(){if(typeof sessionStorage>`u`)return Date.now();let e=sessionStorage.getItem(`session_start`);if(e)return Number.parseInt(e,10);let t=sessionStorage.getItem(`session_timestamp`);return t?Number.parseInt(t,10):Date.now()}function l(e){return typeof e!=`number`||!Number.isFinite(e)?100:Math.max(0,Math.min(100,e))}export{s as a,t as c,c as i,n,i as o,a as r,o as s,l as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e,t}from"./types-B5zbhOWK.js";import{onCLS as n,onFCP as r,onINP as i,onLCP as a,onTTFB as o}from"web-vitals/attribution";var s=class{endpoint;siteKey;debug;samplingPercentage;started=!1;sessionSamplingSeed;metricsMap=new Map;flushed=!1;constructor(e){this.siteKey=e.siteKey,this.endpoint=this.getVitalsEndpoint(e.endpoint??`https://metrics.faststats.dev/v1/web`),this.debug=e.debug??!1,this.samplingPercentage=t(e.samplingPercentage),this.sessionSamplingSeed=Math.random()*100}getVitalsEndpoint(e){let t=new URL(e),n=t.pathname.split(`/`);return n[n.length-1]=`vitals`,t.pathname=n.join(`/`),t.toString()}start(){this.started||typeof window>`u`||(this.started=!0,document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.finalizeAndFlush()}),window.addEventListener(`pagehide`,()=>{this.finalizeAndFlush()}),this.debug&&console.log(`[WebVitals] Tracking started`),n(e=>this.captureMetric(e)),i(e=>this.captureMetric(e)),a(e=>this.captureMetric(e)),r(e=>this.captureMetric(e)),o(e=>this.captureMetric(e)))}captureMetric(e){if(this.flushed||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;let t=e.name,n={id:e.id,rating:e.rating,delta:e.delta,navigationType:e.navigationType,...e.attribution??{}},r=t===`FCP`||t===`TTFB`;this.metricsMap.set(t,{value:e.value,attributes:n,final:r}),this.debug&&console.log(`[WebVitals] ${t} captured: ${e.value}`+(r?` (final)`:``))}finalizeAndFlush(){if(!(this.flushed||this.metricsMap.size===0)){for(let[e,t]of this.metricsMap.entries())t.final||(t.final=!0,this.debug&&console.log(`[WebVitals] ${e} finalized: ${t.value}`));this.flushWithBeacon()}}buildPayload(){if(this.metricsMap.size===0)return null;let t=Array.from(this.metricsMap.entries()).map(([e,t])=>({metric:e,value:t.value,attributes:t.attributes}));return{body:JSON.stringify({sessionId:e(),vitals:t,metadata:{url:window.location.href}}),count:t.length}}flushWithBeacon(){let e=this.buildPayload();if(e){if(this.flushed=!0,this.debug){let t=Array.from(this.metricsMap.keys()).join(`, `);console.log(`[WebVitals] Sending final metrics (${e.count}): ${t}`)}fetch(this.endpoint,{method:`POST`,body:e.body,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.siteKey}`},keepalive:!0}).catch(()=>{this.debug&&console.warn(`[WebVitals] Failed to send metrics`)})}}};export{s as default};
|
package/package.json
CHANGED
package/src/analytics.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
refreshSessionTimestamp,
|
|
7
7
|
resetAnonymousId,
|
|
8
8
|
resetSessionId,
|
|
9
|
+
setCookielessMode,
|
|
9
10
|
} from "./utils/identifiers";
|
|
10
11
|
import {
|
|
11
12
|
normalizeSamplingPercentage,
|
|
@@ -234,7 +235,7 @@ export class WebAnalytics {
|
|
|
234
235
|
this.started = true;
|
|
235
236
|
_instance = this;
|
|
236
237
|
|
|
237
|
-
|
|
238
|
+
setCookielessMode(this.isCookielessMode());
|
|
238
239
|
const opts = this.options;
|
|
239
240
|
|
|
240
241
|
if (opts.errorTracking?.enabled ?? opts.trackErrors) {
|
|
@@ -243,14 +244,6 @@ export class WebAnalytics {
|
|
|
243
244
|
siteKey: opts.siteKey,
|
|
244
245
|
endpoint: this.endpoint,
|
|
245
246
|
debug: this.debug,
|
|
246
|
-
getCommonData: () => ({
|
|
247
|
-
url: location.href,
|
|
248
|
-
page: location.pathname,
|
|
249
|
-
referrer: document.referrer || null,
|
|
250
|
-
}),
|
|
251
|
-
getAnonymousId: getAnonymousIdFn,
|
|
252
|
-
getSessionId: getOrCreateSessionId,
|
|
253
|
-
sendData,
|
|
254
247
|
}).start();
|
|
255
248
|
this.log("error loaded");
|
|
256
249
|
}
|
|
@@ -263,7 +256,6 @@ export class WebAnalytics {
|
|
|
263
256
|
samplingPercentage: normalizeSamplingPercentage(
|
|
264
257
|
opts.webVitals?.sampling?.percentage,
|
|
265
258
|
),
|
|
266
|
-
getSessionId: getOrCreateSessionId,
|
|
267
259
|
}).start();
|
|
268
260
|
this.log("web-vitals loaded");
|
|
269
261
|
}
|
|
@@ -273,9 +265,7 @@ export class WebAnalytics {
|
|
|
273
265
|
siteKey: opts.siteKey,
|
|
274
266
|
endpoint: this.endpoint,
|
|
275
267
|
debug: this.debug,
|
|
276
|
-
|
|
277
|
-
getSessionId: getOrCreateSessionId,
|
|
278
|
-
sendData,
|
|
268
|
+
...opts.replayOptions,
|
|
279
269
|
}).start();
|
|
280
270
|
this.log("replay loaded");
|
|
281
271
|
}
|
|
@@ -354,6 +344,7 @@ export class WebAnalytics {
|
|
|
354
344
|
|
|
355
345
|
setConsentMode(mode: ConsentMode): void {
|
|
356
346
|
this.consentMode = mode;
|
|
347
|
+
setCookielessMode(this.isCookielessMode());
|
|
357
348
|
}
|
|
358
349
|
|
|
359
350
|
optIn(): void {
|
package/src/error.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { sendData } from "./analytics";
|
|
2
|
+
import { getAnonymousId, getOrCreateSessionId } from "./utils/identifiers";
|
|
2
3
|
|
|
3
4
|
export type ErrorEntry = {
|
|
4
5
|
error: string;
|
|
@@ -27,10 +28,6 @@ export interface ErrorTrackingOptions {
|
|
|
27
28
|
debug?: boolean;
|
|
28
29
|
flushInterval?: number;
|
|
29
30
|
maxQueueSize?: number;
|
|
30
|
-
getCommonData?: () => Record<string, unknown>;
|
|
31
|
-
getAnonymousId?: () => string;
|
|
32
|
-
getSessionId?: () => string;
|
|
33
|
-
sendData?: (options: SendDataOptions) => Promise<boolean>;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
class ErrorTracker {
|
|
@@ -39,10 +36,6 @@ class ErrorTracker {
|
|
|
39
36
|
private readonly debug: boolean;
|
|
40
37
|
private readonly flushInterval: number;
|
|
41
38
|
private readonly maxQueueSize: number;
|
|
42
|
-
private readonly getCommonData: () => Record<string, unknown>;
|
|
43
|
-
private readonly getAnonymousId: () => string;
|
|
44
|
-
private readonly getSessionId: () => string;
|
|
45
|
-
private readonly sendData: (options: SendDataOptions) => Promise<boolean>;
|
|
46
39
|
|
|
47
40
|
private readonly handledErrors = new WeakSet<Error>();
|
|
48
41
|
private readonly errorCounts = new Map<
|
|
@@ -58,10 +51,6 @@ class ErrorTracker {
|
|
|
58
51
|
this.debug = options.debug ?? false;
|
|
59
52
|
this.flushInterval = options.flushInterval ?? 5000;
|
|
60
53
|
this.maxQueueSize = options.maxQueueSize ?? 50;
|
|
61
|
-
this.getCommonData = options.getCommonData ?? (() => ({}));
|
|
62
|
-
this.getAnonymousId = options.getAnonymousId ?? (() => "");
|
|
63
|
-
this.getSessionId = options.getSessionId ?? (() => "");
|
|
64
|
-
this.sendData = options.sendData ?? (async () => false);
|
|
65
54
|
}
|
|
66
55
|
|
|
67
56
|
start(): void {
|
|
@@ -268,8 +257,15 @@ class ErrorTracker {
|
|
|
268
257
|
console.log("[ErrorTracker] Flushing errors:", errors);
|
|
269
258
|
}
|
|
270
259
|
|
|
271
|
-
const
|
|
272
|
-
const
|
|
260
|
+
const identifier = getAnonymousId();
|
|
261
|
+
const commonData =
|
|
262
|
+
typeof document !== "undefined"
|
|
263
|
+
? {
|
|
264
|
+
url: location.href,
|
|
265
|
+
page: location.pathname,
|
|
266
|
+
referrer: document.referrer || null,
|
|
267
|
+
}
|
|
268
|
+
: { url: "", page: "", referrer: null };
|
|
273
269
|
const sourcemapsBuild = (
|
|
274
270
|
globalThis as {
|
|
275
271
|
__SOURCEMAPS_BUILD__?: { buildId?: unknown };
|
|
@@ -284,7 +280,7 @@ class ErrorTracker {
|
|
|
284
280
|
const payload: Record<string, unknown> = {
|
|
285
281
|
token: this.siteKey,
|
|
286
282
|
...(identifier ? { userId: identifier } : {}),
|
|
287
|
-
sessionId:
|
|
283
|
+
sessionId: getOrCreateSessionId(),
|
|
288
284
|
...(buildId ? { buildId } : {}),
|
|
289
285
|
data: {
|
|
290
286
|
url: commonData.url as string,
|
|
@@ -301,7 +297,7 @@ class ErrorTracker {
|
|
|
301
297
|
console.log("[ErrorTracker] Payload:", payloadString);
|
|
302
298
|
}
|
|
303
299
|
|
|
304
|
-
|
|
300
|
+
sendData({
|
|
305
301
|
url: this.endpoint,
|
|
306
302
|
data: payloadString,
|
|
307
303
|
debug: this.debug,
|
package/src/replay.ts
CHANGED
|
@@ -3,10 +3,9 @@ import type { eventWithTime, listenerHandler } from "@rrweb/types";
|
|
|
3
3
|
import { record } from "rrweb";
|
|
4
4
|
import type { recordOptions } from "rrweb/typings/types";
|
|
5
5
|
import type { SlimDOMOptions } from "rrweb-snapshot";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "./utils/types";
|
|
6
|
+
import { sendData } from "./analytics";
|
|
7
|
+
import { getAnonymousId, getOrCreateSessionId } from "./utils/identifiers";
|
|
8
|
+
import { normalizeSamplingPercentage } from "./utils/types";
|
|
10
9
|
|
|
11
10
|
export interface ReplayTrackerOptions {
|
|
12
11
|
siteKey: string;
|
|
@@ -15,6 +14,7 @@ export interface ReplayTrackerOptions {
|
|
|
15
14
|
samplingPercentage?: number;
|
|
16
15
|
flushInterval?: number;
|
|
17
16
|
maxEvents?: number;
|
|
17
|
+
minReplayLengthMs?: number;
|
|
18
18
|
sampling?: recordOptions<eventWithTime>["sampling"];
|
|
19
19
|
slimDOMOptions?: SlimDOMOptions;
|
|
20
20
|
maskAllInputs?: boolean;
|
|
@@ -26,9 +26,6 @@ export interface ReplayTrackerOptions {
|
|
|
26
26
|
checkoutEveryNms?: number;
|
|
27
27
|
checkoutEveryNth?: number;
|
|
28
28
|
recordConsole?: boolean;
|
|
29
|
-
getAnonymousId?: () => string;
|
|
30
|
-
getSessionId?: () => string;
|
|
31
|
-
sendData?: (options: SendDataOptions) => Promise<boolean>;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
interface PendingBatch {
|
|
@@ -68,15 +65,13 @@ class ReplayTracker {
|
|
|
68
65
|
private stopRecording: listenerHandler | undefined = undefined;
|
|
69
66
|
private started = false;
|
|
70
67
|
private startTime: number = 0;
|
|
68
|
+
private minLengthFlushScheduled = false;
|
|
71
69
|
private sequenceNumber = 0;
|
|
72
70
|
private pendingBatches: PendingBatch[] = [];
|
|
73
71
|
private isFlushing = false;
|
|
74
72
|
private compressionSupported = false;
|
|
75
73
|
private readonly sessionSamplingSeed: number;
|
|
76
|
-
private readonly minReplayLengthMs
|
|
77
|
-
private readonly _getAnonymousId: () => string;
|
|
78
|
-
private readonly _getSessionId: () => string;
|
|
79
|
-
private readonly sendData: (options: SendDataOptions) => Promise<boolean>;
|
|
74
|
+
private readonly minReplayLengthMs: number;
|
|
80
75
|
|
|
81
76
|
constructor(options: ReplayTrackerOptions) {
|
|
82
77
|
this.siteKey = options.siteKey;
|
|
@@ -120,10 +115,8 @@ class ReplayTracker {
|
|
|
120
115
|
this.checkoutEveryNms = options.checkoutEveryNms ?? 60_000;
|
|
121
116
|
this.checkoutEveryNth = options.checkoutEveryNth;
|
|
122
117
|
this.recordConsole = options.recordConsole ?? true;
|
|
118
|
+
this.minReplayLengthMs = options.minReplayLengthMs ?? 3000;
|
|
123
119
|
this.sessionSamplingSeed = Math.random() * 100;
|
|
124
|
-
this._getAnonymousId = options.getAnonymousId ?? (() => "");
|
|
125
|
-
this._getSessionId = options.getSessionId ?? (() => "");
|
|
126
|
-
this.sendData = options.sendData ?? (async () => false);
|
|
127
120
|
|
|
128
121
|
if (typeof window !== "undefined") {
|
|
129
122
|
this.compressionSupported = "CompressionStream" in window;
|
|
@@ -210,12 +203,11 @@ class ReplayTracker {
|
|
|
210
203
|
this.handleVisibilityChange,
|
|
211
204
|
);
|
|
212
205
|
|
|
213
|
-
|
|
214
|
-
if (elapsedTime < this.minReplayLengthMs) {
|
|
206
|
+
if (!this.hasReachedMinLength()) {
|
|
215
207
|
this.events = [];
|
|
216
208
|
if (this.debug) {
|
|
217
209
|
console.log(
|
|
218
|
-
`[Replay] Session too short (${
|
|
210
|
+
`[Replay] Session too short (${Date.now() - this.startTime}ms), discarding events`,
|
|
219
211
|
);
|
|
220
212
|
}
|
|
221
213
|
return;
|
|
@@ -231,9 +223,17 @@ class ReplayTracker {
|
|
|
231
223
|
this.scheduleFlush();
|
|
232
224
|
} else if (this.events.length >= this.maxEvents) {
|
|
233
225
|
this.scheduleFlush();
|
|
226
|
+
} else if (!this.minLengthFlushScheduled && this.hasReachedMinLength()) {
|
|
227
|
+
this.minLengthFlushScheduled = true;
|
|
228
|
+
this.scheduleFlush();
|
|
234
229
|
}
|
|
235
230
|
}
|
|
236
231
|
|
|
232
|
+
private hasReachedMinLength(): boolean {
|
|
233
|
+
if (this.minReplayLengthMs <= 0) return true;
|
|
234
|
+
return Date.now() - this.startTime >= this.minReplayLengthMs;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
237
|
private scheduleFlush(): void {
|
|
238
238
|
if (this.isFlushing || this.events.length === 0) return;
|
|
239
239
|
|
|
@@ -270,10 +270,11 @@ class ReplayTracker {
|
|
|
270
270
|
return;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
if (elapsedTime < this.minReplayLengthMs) {
|
|
273
|
+
if (!this.hasReachedMinLength()) {
|
|
275
274
|
if (this.debug) {
|
|
276
|
-
console.log(
|
|
275
|
+
console.log(
|
|
276
|
+
`[Replay] Too short (${Date.now() - this.startTime}ms), skipping`,
|
|
277
|
+
);
|
|
277
278
|
}
|
|
278
279
|
this.processPendingBatches();
|
|
279
280
|
return;
|
|
@@ -285,10 +286,10 @@ class ReplayTracker {
|
|
|
285
286
|
const eventsToSend = this.events;
|
|
286
287
|
this.events = [];
|
|
287
288
|
|
|
288
|
-
const anonymousId =
|
|
289
|
+
const anonymousId = getAnonymousId();
|
|
289
290
|
const batch = {
|
|
290
291
|
token: this.siteKey,
|
|
291
|
-
sessionId:
|
|
292
|
+
sessionId: getOrCreateSessionId(),
|
|
292
293
|
...(anonymousId ? { identifier: anonymousId } : {}),
|
|
293
294
|
sequence: this.sequenceNumber++,
|
|
294
295
|
timestamp: Date.now(),
|
|
@@ -446,7 +447,7 @@ class ReplayTracker {
|
|
|
446
447
|
const sizeKB = (data.size / 1024).toFixed(1);
|
|
447
448
|
|
|
448
449
|
return (
|
|
449
|
-
|
|
450
|
+
sendData({
|
|
450
451
|
url,
|
|
451
452
|
data,
|
|
452
453
|
contentType: isCompressed
|
|
@@ -460,7 +461,7 @@ class ReplayTracker {
|
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
getSessionId(): string | undefined {
|
|
463
|
-
return
|
|
464
|
+
return getOrCreateSessionId();
|
|
464
465
|
}
|
|
465
466
|
}
|
|
466
467
|
|
package/src/utils/identifiers.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
const SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
2
2
|
|
|
3
|
+
let cookielessMode = false;
|
|
4
|
+
|
|
5
|
+
export function setCookielessMode(mode: boolean): void {
|
|
6
|
+
cookielessMode = mode;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export function getAnonymousId(cookieless?: boolean): string {
|
|
4
|
-
|
|
10
|
+
const useCookieless = cookieless ?? cookielessMode;
|
|
11
|
+
if (useCookieless) return "";
|
|
5
12
|
return getOrCreateAnonymousId();
|
|
6
13
|
}
|
|
7
14
|
|
package/src/web-vitals.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
onLCP,
|
|
7
7
|
onTTFB,
|
|
8
8
|
} from "web-vitals/attribution";
|
|
9
|
+
import { getOrCreateSessionId } from "./utils/identifiers";
|
|
9
10
|
import { normalizeSamplingPercentage } from "./utils/types";
|
|
10
11
|
|
|
11
12
|
export interface WebVitalsOptions {
|
|
@@ -13,7 +14,6 @@ export interface WebVitalsOptions {
|
|
|
13
14
|
endpoint?: string;
|
|
14
15
|
debug?: boolean;
|
|
15
16
|
samplingPercentage?: number;
|
|
16
|
-
getSessionId?: () => string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
type MetricName = "CLS" | "INP" | "LCP" | "FCP" | "TTFB";
|
|
@@ -34,7 +34,6 @@ class WebVitalsTracker {
|
|
|
34
34
|
private readonly sessionSamplingSeed: number;
|
|
35
35
|
private readonly metricsMap = new Map<MetricName, MetricState>();
|
|
36
36
|
private flushed = false;
|
|
37
|
-
private readonly getSessionId: () => string;
|
|
38
37
|
|
|
39
38
|
constructor(options: WebVitalsOptions) {
|
|
40
39
|
this.siteKey = options.siteKey;
|
|
@@ -46,7 +45,6 @@ class WebVitalsTracker {
|
|
|
46
45
|
options.samplingPercentage,
|
|
47
46
|
);
|
|
48
47
|
this.sessionSamplingSeed = Math.random() * 100;
|
|
49
|
-
this.getSessionId = options.getSessionId ?? (() => "");
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
private getVitalsEndpoint(baseEndpoint: string): string {
|
|
@@ -149,7 +147,7 @@ class WebVitalsTracker {
|
|
|
149
147
|
|
|
150
148
|
return {
|
|
151
149
|
body: JSON.stringify({
|
|
152
|
-
sessionId:
|
|
150
|
+
sessionId: getOrCreateSessionId(),
|
|
153
151
|
vitals,
|
|
154
152
|
metadata: {
|
|
155
153
|
url: window.location.href,
|
package/dist/error-DtTBFJh_.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
var e=class{endpoint;siteKey;debug;flushInterval;maxQueueSize;getCommonData;getAnonymousId;getSessionId;sendData;handledErrors=new WeakSet;errorCounts=new Map;flushTimer=null;started=!1;constructor(e){this.siteKey=e.siteKey,this.endpoint=e.endpoint??`https://metrics.faststats.dev/v1/web`,this.debug=e.debug??!1,this.flushInterval=e.flushInterval??5e3,this.maxQueueSize=e.maxQueueSize??50,this.getCommonData=e.getCommonData??(()=>({})),this.getAnonymousId=e.getAnonymousId??(()=>``),this.getSessionId=e.getSessionId??(()=>``),this.sendData=e.sendData??(async()=>!1)}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.handleErrorEvent),window.addEventListener(`unhandledrejection`,this.handleRejection),this.flushTimer=setInterval(()=>this.flush(),this.flushInterval),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()}),window.addEventListener(`pagehide`,()=>this.flush()),this.debug&&console.log(`[ErrorTracker] Started listening for errors`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.handleErrorEvent),window.removeEventListener(`unhandledrejection`,this.handleRejection),this.flushTimer&&=(clearInterval(this.flushTimer),null),this.flush(),this.debug&&console.log(`[ErrorTracker] Stopped listening for errors`))}handleErrorEvent=e=>{let t=e.error;if(t instanceof Error){if(this.handledErrors.has(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate error:`,t.message);return}this.handledErrors.add(t)}this.queueError({message:e.message||(t?.message??`Unknown error`),filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:t?.stack||void 0,type:`error`})};handleRejection=e=>{let t=e.reason;if(t instanceof Error){if(this.handledErrors.has(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate rejection:`,t.message);return}this.handledErrors.add(t)}this.queueError({message:t instanceof Error?t.message:typeof t==`string`?t:`Unhandled promise rejection`,stack:t instanceof Error?t.stack:void 0,type:`unhandledrejection`})};isExtensionError(e){return!!(e.filename?.startsWith(`chrome-extension://`)||e.stack&&e.stack.split(`
|
|
2
|
-
`).find(e=>e.trim().startsWith(`at `))?.includes(`chrome-extension://`))}parseStack(e){if(e)return e.split(`
|
|
3
|
-
`).map(e=>e.trim()).filter(e=>e.length>0)}async generateErrorHash(e){let t=[e.type,e.message,e.filename??``,e.lineno??``].join(`:`),n=new TextEncoder().encode(t),r=await crypto.subtle.digest(`SHA-256`,n);return`err_${Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}`}async queueError(e){if(this.isExtensionError(e))return;this.debug&&console.log(`[ErrorTracker] Captured error:`,e);let t=await this.generateErrorHash(e),n=this.errorCounts.get(t);if(n)n.count++,this.debug&&console.log(`[ErrorTracker] Incremented count for ${t} to ${n.count}`);else{let n={error:e.type===`unhandledrejection`?`UnhandledRejection`:`Error`,message:e.message,stack:this.parseStack(e.stack)};this.errorCounts.set(t,{entry:n,hash:t,count:1}),this.debug&&console.log(`[ErrorTracker] Queued new error: ${t}`)}this.errorCounts.size>=this.maxQueueSize&&this.flush()}captureError(e){if(this.handledErrors.has(e)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate manual capture:`,e.message);return}this.handledErrors.add(e),this.queueError({message:e.message,stack:e.stack,type:`error`})}flush(){if(this.errorCounts.size===0)return;let e=[];for(let{entry:t,hash:n,count:r}of this.errorCounts.values())e.push({...t,hash:n,count:r});this.errorCounts.clear(),this.debug&&console.log(`[ErrorTracker] Flushing errors:`,e);let t=this.getCommonData(),n=this.getAnonymousId(),r=globalThis.__SOURCEMAPS_BUILD__,i=typeof r?.buildId==`string`&&r.buildId.trim().length>0?r.buildId:void 0,a={token:this.siteKey,...n?{userId:n}:{},sessionId:this.getSessionId(),...i?{buildId:i}:{},data:{url:t.url,page:t.page,referrer:t.referrer,title:typeof document<`u`?document.title:``},errors:e},o=JSON.stringify(a);this.debug&&console.log(`[ErrorTracker] Payload:`,o),this.sendData({url:this.endpoint,data:o,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{e as default};
|
package/dist/replay-ZmimkIBT.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./types-E07i1PxI.js";import{getRecordConsolePlugin as t}from"@rrweb/rrweb-plugin-console-record";import{record as n}from"rrweb";var r=class{endpoint;siteKey;debug;flushInterval;maxEvents;sampling;slimDOMOptions;maskAllInputs;maskInputOptions;blockClass;blockSelector;maskTextClass;maskTextSelector;checkoutEveryNms;checkoutEveryNth;samplingPercentage;recordConsole;events=[];flushTimer=null;stopRecording=void 0;started=!1;startTime=0;sequenceNumber=0;pendingBatches=[];isFlushing=!1;compressionSupported=!1;sessionSamplingSeed;minReplayLengthMs=3e3;_getAnonymousId;_getSessionId;sendData;constructor(t){this.siteKey=t.siteKey,this.endpoint=t.endpoint?.replace(/\/v1\/web$/,`/v1/replay`)??`https://metrics.faststats.dev/v1/replay`,this.debug=t.debug??!1,this.samplingPercentage=e(t.samplingPercentage),this.flushInterval=t.flushInterval??1e4,this.maxEvents=t.maxEvents??500,this.sampling=t.sampling??{mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},this.slimDOMOptions=t.slimDOMOptions??{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0},this.maskAllInputs=t.maskAllInputs??!0,this.maskInputOptions=t.maskInputOptions??{password:!0,email:!0,tel:!0},this.blockClass=t.blockClass,this.blockSelector=t.blockSelector,this.maskTextClass=t.maskTextClass,this.maskTextSelector=t.maskTextSelector,this.checkoutEveryNms=t.checkoutEveryNms??6e4,this.checkoutEveryNth=t.checkoutEveryNth,this.recordConsole=t.recordConsole??!0,this.sessionSamplingSeed=Math.random()*100,this._getAnonymousId=t.getAnonymousId??(()=>``),this._getSessionId=t.getSessionId??(()=>``),this.sendData=t.sendData??(async()=>!1),typeof window<`u`&&(this.compressionSupported=`CompressionStream`in window)}start(){if(this.started||typeof window>`u`||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;this.started=!0,this.startTime=Date.now(),this.debug&&console.log(`[Replay] Recording started`);let e={emit:(e,t)=>this.handleEvent(e,t),sampling:this.sampling,slimDOMOptions:this.slimDOMOptions,maskAllInputs:this.maskAllInputs,checkoutEveryNms:this.checkoutEveryNms};this.maskInputOptions&&(e.maskInputOptions=this.maskInputOptions),this.blockClass&&(e.blockClass=this.blockClass),this.blockSelector&&(e.blockSelector=this.blockSelector),this.maskTextClass&&(e.maskTextClass=this.maskTextClass),this.maskTextSelector&&(e.maskTextSelector=this.maskTextSelector),this.checkoutEveryNth&&(e.checkoutEveryNth=this.checkoutEveryNth),this.recordConsole&&(e.plugins=[t()]),this.stopRecording=n(e),this.flushTimer=setInterval(()=>{this.scheduleFlush()},this.flushInterval),window.addEventListener(`beforeunload`,this.handleUnload),window.addEventListener(`pagehide`,this.handleUnload),document.addEventListener(`visibilitychange`,this.handleVisibilityChange)}stop(){if(!this.started)return;this.started=!1,this.debug&&console.log(`[Replay] Recording stopped`),this.stopRecording?.(),this.stopRecording=void 0,this.flushTimer&&=(clearInterval(this.flushTimer),null),window.removeEventListener(`beforeunload`,this.handleUnload),window.removeEventListener(`pagehide`,this.handleUnload),document.removeEventListener(`visibilitychange`,this.handleVisibilityChange);let e=Date.now()-this.startTime;if(e<this.minReplayLengthMs){this.events=[],this.debug&&console.log(`[Replay] Session too short (${e}ms), discarding events`);return}this.flush()}handleEvent(e,t){this.events.push(e),(t||this.events.length>=this.maxEvents)&&this.scheduleFlush()}scheduleFlush(){this.isFlushing||this.events.length===0||(typeof window<`u`&&`requestIdleCallback`in window?window.requestIdleCallback(()=>this.flush(),{timeout:2e3}):setTimeout(()=>this.flush(),0))}handleUnload=()=>{this.flush()};handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.flushTimer&&=(clearInterval(this.flushTimer),null),this.flush()):document.visibilityState===`visible`&&this.started&&(this.flushTimer||=setInterval(()=>{this.scheduleFlush()},this.flushInterval))};async flush(){if(this.events.length===0){this.processPendingBatches();return}let e=Date.now()-this.startTime;if(e<this.minReplayLengthMs){this.debug&&console.log(`[Replay] Too short (${e}ms), skipping`),this.processPendingBatches();return}if(this.isFlushing)return;this.isFlushing=!0;let t=this.events;this.events=[];let n=this._getAnonymousId(),r={token:this.siteKey,sessionId:this._getSessionId(),...n?{identifier:n}:{},sequence:this.sequenceNumber++,timestamp:Date.now(),url:window.location.href,events:t};this.debug&&console.log(`[Replay] Sending ${t.length} events (seq: ${r.sequence})`);try{let e,t=!1;if(this.compressionSupported)try{e=await this.compress(JSON.stringify(r)),t=!0}catch{this.debug&&console.warn(`[Replay] Compression failed, using uncompressed`),e=new Blob([JSON.stringify(r)],{type:`application/json`})}else e=new Blob([JSON.stringify(r)],{type:`application/json`});await this.send(e,t)||this.pendingBatches.push({batch:r,isCompressed:t,retries:0})}catch(e){this.debug&&console.warn(`[Replay] Flush error:`,e),this.pendingBatches.push({batch:r,isCompressed:!1,retries:0})}finally{this.isFlushing=!1,this.processPendingBatches()}}async processPendingBatches(){if(this.pendingBatches.length===0)return;let e=this.pendingBatches.shift();if(e){if(e.retries>=3){this.debug&&console.warn(`[Replay] Max retries reached, dropping batch ${e.batch.sequence}`),this.processPendingBatches();return}e.retries++;try{let t;if(e.isCompressed&&this.compressionSupported)try{t=await this.compress(JSON.stringify(e.batch))}catch{t=new Blob([JSON.stringify(e.batch)],{type:`application/json`}),e.isCompressed=!1}else t=new Blob([JSON.stringify(e.batch)],{type:`application/json`});await this.send(t,e.isCompressed)?this.processPendingBatches():(this.pendingBatches.push(e),setTimeout(()=>this.processPendingBatches(),1e3*e.retries))}catch{this.debug&&console.warn(`[Replay] Retry ${e.retries} failed`),this.pendingBatches.push(e),setTimeout(()=>this.processPendingBatches(),1e3*e.retries)}}}async compress(e){if(!this.compressionSupported)throw Error(`Compression not supported`);let t=new TextEncoder().encode(e),n=new CompressionStream(`gzip`),r=n.writable.getWriter();r.write(t),r.close();let i=[],a=n.readable.getReader();for(;;){let{done:e,value:t}=await a.read();if(e)break;t&&i.push(t)}let o=i.reduce((e,t)=>e+t.length,0),s=new Uint8Array(o),c=0;for(let e of i)s.set(e,c),c+=e.length;if(this.debug){let e=(s.length/t.length*100).toFixed(1);console.log(`[Replay] Compressed: ${t.length} → ${s.length} bytes (${e}%)`)}return new Blob([s],{type:`application/octet-stream`})}async send(e,t){let n=t?`${this.endpoint}?encoding=gzip`:this.endpoint,r=(e.size/1024).toFixed(1);return this.sendData({url:n,data:e,contentType:t?`application/octet-stream`:`application/json`,headers:t?{"Content-Encoding":`gzip`}:void 0,debug:this.debug,debugPrefix:`[Replay] ${r}KB`})??Promise.resolve(!1)}getSessionId(){return this._getSessionId()}};export{r as default};
|
package/dist/types-E07i1PxI.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function e(e){return typeof e!=`number`||!Number.isFinite(e)?100:Math.max(0,Math.min(100,e))}export{e as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./types-E07i1PxI.js";import{onCLS as t,onFCP as n,onINP as r,onLCP as i,onTTFB as a}from"web-vitals/attribution";var o=class{endpoint;siteKey;debug;samplingPercentage;started=!1;sessionSamplingSeed;metricsMap=new Map;flushed=!1;getSessionId;constructor(t){this.siteKey=t.siteKey,this.endpoint=this.getVitalsEndpoint(t.endpoint??`https://metrics.faststats.dev/v1/web`),this.debug=t.debug??!1,this.samplingPercentage=e(t.samplingPercentage),this.sessionSamplingSeed=Math.random()*100,this.getSessionId=t.getSessionId??(()=>``)}getVitalsEndpoint(e){let t=new URL(e),n=t.pathname.split(`/`);return n[n.length-1]=`vitals`,t.pathname=n.join(`/`),t.toString()}start(){this.started||typeof window>`u`||(this.started=!0,document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.finalizeAndFlush()}),window.addEventListener(`pagehide`,()=>{this.finalizeAndFlush()}),this.debug&&console.log(`[WebVitals] Tracking started`),t(e=>this.captureMetric(e)),r(e=>this.captureMetric(e)),i(e=>this.captureMetric(e)),n(e=>this.captureMetric(e)),a(e=>this.captureMetric(e)))}captureMetric(e){if(this.flushed||this.samplingPercentage<100&&this.sessionSamplingSeed>=this.samplingPercentage)return;let t=e.name,n={id:e.id,rating:e.rating,delta:e.delta,navigationType:e.navigationType,...e.attribution??{}},r=t===`FCP`||t===`TTFB`;this.metricsMap.set(t,{value:e.value,attributes:n,final:r}),this.debug&&console.log(`[WebVitals] ${t} captured: ${e.value}`+(r?` (final)`:``))}finalizeAndFlush(){if(!(this.flushed||this.metricsMap.size===0)){for(let[e,t]of this.metricsMap.entries())t.final||(t.final=!0,this.debug&&console.log(`[WebVitals] ${e} finalized: ${t.value}`));this.flushWithBeacon()}}buildPayload(){if(this.metricsMap.size===0)return null;let e=Array.from(this.metricsMap.entries()).map(([e,t])=>({metric:e,value:t.value,attributes:t.attributes}));return{body:JSON.stringify({sessionId:this.getSessionId(),vitals:e,metadata:{url:window.location.href}}),count:e.length}}flushWithBeacon(){let e=this.buildPayload();if(e){if(this.flushed=!0,this.debug){let t=Array.from(this.metricsMap.keys()).join(`, `);console.log(`[WebVitals] Sending final metrics (${e.count}): ${t}`)}fetch(this.endpoint,{method:`POST`,body:e.body,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.siteKey}`},keepalive:!0}).catch(()=>{this.debug&&console.warn(`[WebVitals] Failed to send metrics`)})}}};export{o as default};
|