@faststats/web 0.1.2 → 0.1.4

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @faststats/web
2
2
 
3
+ ## 0.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 376922e: updates
8
+
9
+ ## 0.1.3
10
+
3
11
  ## 0.1.2
4
12
 
5
13
  ### Patch Changes
@@ -0,0 +1 @@
1
+ import{a as e,c as t,f as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./types-8FXsUqbi.js";let d=null;function f(){return d}function p(e,t){typeof window>`u`||b()||d?.track(e,t??{})}function m(e,t,n){typeof window>`u`||b()||d?.identify(e,t,n??{})}function h(e=!0){typeof window>`u`||b()||d?.logout(e)}function g(e){d?.setConsentMode(e)}function _(){g(`granted`)}function v(){g(`denied`)}function y(e){typeof window>`u`||b()||d?.reportError(e)}function b(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function x(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 S(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function C(){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 w=class{webEndpoint;baseUrl;debug;started=!1;destroyed=!1;pageKey=``;navTimer=null;heartbeatTimer=null;scrollDepth=0;pageEntryTime=0;pagePath=``;pageUrl=``;pageHash=``;hasLeftCurrentPage=!1;scrollHandler=null;consentMode;cookielessWhilePending;cleanupCallbacks=[];childTrackers=[];pendingReportedErrors=[];handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.leavePage(),this.stopHeartbeat()):this.startHeartbeat()};handlePageHide=()=>{this.leavePage()};handlePopState=()=>{this.navigate()};handleHashChange=()=>{this.navigate()};constructor(e){this.options=e,this.baseUrl=u(e.baseUrl),this.webEndpoint=n(this.baseUrl),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(b()){this.log(`disabled`);return}d=this,t(this.isCookielessMode()),setTimeout(()=>void this.start(),0)}}registerCleanup(e){this.cleanupCallbacks.push(e)}addWindowListener(e,t){window.addEventListener(e,t),this.registerCleanup(()=>window.removeEventListener(e,t))}addDocumentListener(e,t){document.addEventListener(e,t),this.registerCleanup(()=>document.removeEventListener(e,t))}patchHistory(){let e=history.pushState.bind(history),t=history.replaceState.bind(history);history.pushState=(t,n,r)=>{e(t,n,r),this.navigate()},history.replaceState=(e,n,r)=>{t(e,n,r),this.navigate()},this.registerCleanup(()=>{history.pushState=e,history.replaceState=t})}ensureStarted(){return typeof window>`u`||this.destroyed||b()?!1:(this.started||this.start(),!0)}stopHeartbeat(){this.heartbeatTimer&&=(clearInterval(this.heartbeatTimer),null)}stopNavigationTimer(){this.navTimer&&=(clearTimeout(this.navTimer),null)}stopChildTrackers(){for(let e of this.childTrackers.splice(0))e.stop?.()}getErrorTracker(){for(let e of this.childTrackers)if(`captureError`in e)return e;return null}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(d&&d!==this){this.log(`already started by another instance`);return}if(b()){this.log(`disabled`);return}this.started=!0,d=this,t(this.isCookielessMode());let e=this.options;try{if(e.trackErrors){let{default:t}=await import(`./error-BNd3BvFI.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug});for(n.start(),this.childTrackers.push(n);this.pendingReportedErrors.length>0;){let e=this.pendingReportedErrors.shift();e&&n.captureError(e)}this.log(`error loaded`)}if(e.trackWebVitals){let{default:t}=await import(`./web-vitals-Bj8FoiTR.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:l(e.webVitals?.sampling?.percentage)});n.start(),this.childTrackers.push(n),this.log(`web-vitals loaded`)}if(e.trackReplay){let{default:t}=await import(`./replay-D9AlW2Jv.js`),n=new t({siteKey:e.siteKey,baseUrl:this.baseUrl,debug:this.debug,...e.replayOptions});n.start(),this.childTrackers.push(n),this.log(`replay loaded`)}}catch(e){this.log(`failed to initialize trackers: ${String(e)}`),this.destroy();return}this.enterPage(),this.pageview({trigger:`load`}),this.links(),this.trackScroll(),this.startHeartbeat(),this.addDocumentListener(`visibilitychange`,this.handleVisibilityChange),this.addWindowListener(`pagehide`,this.handlePageHide),this.addWindowListener(`popstate`,this.handlePopState),e.trackHash&&this.addWindowListener(`hashchange`,this.handleHashChange),this.patchHistory()}destroy(){if(!this.destroyed){for(this.started&&typeof window<`u`&&this.leavePage(),this.pendingReportedErrors.length=0,this.stopNavigationTimer(),this.stopHeartbeat(),this.scrollHandler&&typeof window<`u`&&(window.removeEventListener(`scroll`,this.scrollHandler),this.scrollHandler=null);this.cleanupCallbacks.length>0;)this.cleanupCallbacks.pop()?.();this.stopChildTrackers(),d===this&&(d=null),this.started=!1,this.destroyed=!0}}pageview(e={}){if(!this.ensureStarted())return;let t=`${location.pathname}|${this.options.trackHash??!1?location.hash:``}`;t!==this.pageKey&&(this.pageKey=t,this.send(`pageview`,e))}track(e,t={}){this.ensureStarted()&&this.send(e,t)}identify(e,t,n={}){if(!this.ensureStarted()||this.isCookielessMode())return;let r=e.trim(),o=t.trim();!r||!o||x({url:i(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:a(!1),externalId:r,email:o,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){this.ensureStarted()&&(e&&o(this.isCookielessMode()),c())}setConsentMode(e){this.consentMode=e,t(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return a(this.isCookielessMode())}getSessionId(){return s()}reportError(e){if(this.destroyed||typeof window>`u`||b()||!(this.options.trackErrors??!1)||!this.ensureStarted())return;let t=this.getErrorTracker();if(t){t.captureError(e);return}this.pendingReportedErrors.length>=50&&this.pendingReportedErrors.shift(),this.pendingReportedErrors.push(e)}isCookielessMode(){return this.options.cookieless||this.consentMode===`denied`?!0:this.consentMode===`pending`?this.cookielessWhilePending:!1}send(e,t={}){if(typeof window>`u`||this.destroyed||b())return;let n=a(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:s(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...C(),...t}});this.log(e),x({url:this.webEndpoint,data:r,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.destroyed||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-r()})}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.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(document.visibilityState===`hidden`){this.stopHeartbeat();return}e()},300*1e3)}navigate(){!this.started||this.destroyed||(this.stopNavigationTimer(),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))}links(){let e=e=>{let t=S(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{h as a,y as c,p as d,b as i,x as l,f as n,_ as o,m as r,v as s,w as t,g as u};
@@ -0,0 +1,2 @@
1
+ import{f as e,n as t,r as n}from"./types-8FXsUqbi.js";import{l as r}from"./analytics-D0RCcMlv.js";const i=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;function a(e){if(e)return e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0)}function o(e){if(e instanceof Error)return{error:e.name?.trim()||`Error`,message:e.message,stack:a(e.stack),cause:o(e.cause)};if(typeof e==`string`)return{error:`Error`,message:e}}function s(e){return e?`${e.error}\0${e.message??``}\0${s(e.cause)}`:``}function c(e){let t=[e.type,e.handled?`handled`:`unhandled`,e.message,e.filename??``,e.lineno??``,s(e.cause)].join(`\0`),n=2166136261,r=3598710387;for(let e=0;e<t.length;e++){let i=t.charCodeAt(e);n=Math.imul(n^i,16777619),r=Math.imul(r^i,2246822519)}return`err_${(n>>>0).toString(16).padStart(8,`0`)}${(r>>>0).toString(16).padStart(8,`0`)}`}var l=class{endpoint;siteKey;debug;flushInterval;maxQueueSize;handledErrors=new WeakSet;errorCounts=new Map;flushTimer=null;started=!1;constructor(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.flushInterval=t.flushInterval??5e3,this.maxQueueSize=t.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`))}skipDuplicate(e){return e instanceof Error?this.handledErrors.has(e)?!0:(this.handledErrors.add(e),!1):!1}handleErrorEvent=e=>{let t=e.error;if(this.skipDuplicate(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate error:`,t.message);return}this.queueError({message:e.message||(t instanceof Error?t.message:`Unknown error`),filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:t instanceof Error?t.stack:void 0,type:`error`,handled:!1,cause:t instanceof Error?o(t.cause):void 0})};handleRejection=e=>{let t=e.reason;if(this.skipDuplicate(t)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate rejection:`,t instanceof Error?t.message:t);return}let n=t instanceof Error?t.message:typeof t==`string`?t:`Unhandled promise rejection`;this.queueError({message:n,stack:t instanceof Error?t.stack:void 0,type:`unhandledrejection`,handled:!1,cause:t instanceof Error?o(t.cause):void 0})};queueError(e){if(i.test(`${e.filename??``}\n${e.stack??``}`))return;this.debug&&console.log(`[ErrorTracker] Captured error:`,e);let t=c(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:a(e.stack),handled:e.handled,...e.cause?{cause:e.cause}:{}};this.errorCounts.set(t,{entry:n,count:1}),this.debug&&console.log(`[ErrorTracker] Queued new error: ${t}`)}this.errorCounts.size>=this.maxQueueSize&&this.flush()}captureError(e){if(this.skipDuplicate(e)){this.debug&&console.log(`[ErrorTracker] Skipping duplicate:`,e.message);return}this.queueError({message:e.message,stack:e.stack,type:`error`,handled:!0,cause:o(e.cause)})}flush(){if(this.errorCounts.size===0)return;let e=[];for(let[t,{entry:n,count:r}]of this.errorCounts)e.push({...n,hash:t,count:r});this.errorCounts.clear(),this.debug&&console.log(`[ErrorTracker] Flushing errors:`,e);let i=t(),a=globalThis.__SOURCEMAPS_BUILD__,o=typeof a?.buildId==`string`&&a.buildId.trim().length>0?a.buildId:void 0,s=JSON.stringify({token:this.siteKey,...i?{userId:i}:{},sessionId:n(),...o?{buildId:o}:{},data:{url:location.href,page:location.pathname,referrer:document.referrer||null,title:document.title},errors:e});this.debug&&console.log(`[ErrorTracker] Payload:`,s),r({url:this.endpoint,data:s,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{l as default};
package/dist/module.d.ts CHANGED
@@ -355,7 +355,7 @@ declare global {
355
355
  //#region src/replay.d.ts
356
356
  interface ReplayTrackerOptions {
357
357
  siteKey: string;
358
- endpoint?: string;
358
+ baseUrl?: string;
359
359
  debug?: boolean;
360
360
  samplingPercentage?: number;
361
361
  flushInterval?: number;
@@ -385,22 +385,10 @@ interface SendDataOptions {
385
385
  }
386
386
  //#endregion
387
387
  //#region src/analytics.d.ts
388
- declare function getInstance(): WebAnalytics | null;
389
- declare function trackEvent(eventName: string, properties?: Record<string, unknown>): void;
390
- declare function identify(externalId: string, email: string, options?: IdentifyOptions): void;
391
- declare function logout(resetAnonymousIdentity?: boolean): void;
392
- declare function setConsentMode(mode: ConsentMode): void;
393
- declare function optIn(): void;
394
- declare function optOut(): void;
395
- declare function isTrackingDisabled(): boolean;
396
- declare function sendData(options: SendDataOptions): Promise<boolean>;
397
388
  type Dict = Record<string, unknown>;
398
389
  interface SamplingOptions {
399
390
  percentage?: number;
400
391
  }
401
- interface ErrorTrackingConfig {
402
- enabled?: boolean;
403
- }
404
392
  interface WebVitalsConfig {
405
393
  sampling?: SamplingOptions;
406
394
  }
@@ -420,7 +408,7 @@ interface IdentifyOptions {
420
408
  }
421
409
  interface WebAnalyticsOptions {
422
410
  siteKey: string;
423
- endpoint?: string;
411
+ baseUrl?: string;
424
412
  debug?: boolean;
425
413
  autoTrack?: boolean;
426
414
  trackHash?: boolean;
@@ -429,16 +417,27 @@ interface WebAnalyticsOptions {
429
417
  trackReplay?: boolean;
430
418
  cookieless?: boolean;
431
419
  consent?: ConsentConfig;
432
- errorTracking?: ErrorTrackingConfig;
433
420
  webVitals?: WebVitalsConfig;
434
421
  sessionReplays?: SessionReplayConfig;
435
422
  replayOptions?: Partial<ReplayTrackerOptions>;
436
423
  }
424
+ declare function getInstance(): WebAnalytics | null;
425
+ declare function trackEvent(eventName: string, properties?: Record<string, unknown>): void;
426
+ declare function identify(externalId: string, email: string, options?: IdentifyOptions): void;
427
+ declare function logout(resetAnonymousIdentity?: boolean): void;
428
+ declare function setConsentMode(mode: ConsentMode): void;
429
+ declare function optIn(): void;
430
+ declare function optOut(): void;
431
+ declare function reportError(error: Error): void;
432
+ declare function isTrackingDisabled(): boolean;
433
+ declare function sendData(options: SendDataOptions): Promise<boolean>;
437
434
  declare class WebAnalytics {
438
435
  private readonly options;
439
- private readonly endpoint;
436
+ private readonly webEndpoint;
437
+ private readonly baseUrl;
440
438
  private readonly debug;
441
439
  private started;
440
+ private destroyed;
442
441
  private pageKey;
443
442
  private navTimer;
444
443
  private heartbeatTimer;
@@ -451,10 +450,27 @@ declare class WebAnalytics {
451
450
  private scrollHandler;
452
451
  private consentMode;
453
452
  private readonly cookielessWhilePending;
453
+ private readonly cleanupCallbacks;
454
+ private readonly childTrackers;
455
+ private readonly pendingReportedErrors;
456
+ private readonly handleVisibilityChange;
457
+ private readonly handlePageHide;
458
+ private readonly handlePopState;
459
+ private readonly handleHashChange;
454
460
  constructor(options: WebAnalyticsOptions);
455
461
  private log;
456
462
  private init;
463
+ private registerCleanup;
464
+ private addWindowListener;
465
+ private addDocumentListener;
466
+ private patchHistory;
467
+ private ensureStarted;
468
+ private stopHeartbeat;
469
+ private stopNavigationTimer;
470
+ private stopChildTrackers;
471
+ private getErrorTracker;
457
472
  start(): Promise<void>;
473
+ destroy(): void;
458
474
  pageview(extra?: Dict): void;
459
475
  track(name: string, extra?: Dict): void;
460
476
  identify(externalId: string, email: string, options?: IdentifyOptions): void;
@@ -465,6 +481,7 @@ declare class WebAnalytics {
465
481
  getConsentMode(): ConsentMode;
466
482
  getAnonymousId(): string;
467
483
  getSessionId(): string;
484
+ reportError(error: Error): void;
468
485
  private isCookielessMode;
469
486
  private send;
470
487
  private enterPage;
@@ -472,8 +489,7 @@ declare class WebAnalytics {
472
489
  private trackScroll;
473
490
  private startHeartbeat;
474
491
  private navigate;
475
- private patch;
476
492
  private links;
477
493
  }
478
494
  //#endregion
479
- export { type ConsentMode, type IdentifyOptions, WebAnalytics, type WebAnalyticsOptions, getInstance, identify, isTrackingDisabled, logout, optIn, optOut, sendData, setConsentMode, trackEvent };
495
+ export { type ConsentMode, type IdentifyOptions, WebAnalytics, type WebAnalyticsOptions, getInstance, identify, isTrackingDisabled, logout, optIn, optOut, reportError, sendData, setConsentMode, trackEvent };
package/dist/module.js CHANGED
@@ -1 +1 @@
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};
1
+ import"./types-8FXsUqbi.js";import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./analytics-D0RCcMlv.js";export{l as WebAnalytics,a as getInstance,s as identify,r as isTrackingDisabled,e as logout,o as optIn,c as optOut,t as reportError,i as sendData,u as setConsentMode,n as trackEvent};
@@ -0,0 +1 @@
1
+ import{d as e,n as t,r as n,t as r}from"./types-8FXsUqbi.js";import{l as i}from"./analytics-D0RCcMlv.js";import{getRecordConsolePlugin as a}from"@rrweb/rrweb-plugin-console-record";import{getRecordSequentialIdPlugin as o}from"@rrweb/rrweb-plugin-sequential-id-record";import{record as s}from"rrweb";var c=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(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.samplingPercentage=r(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.minReplayLengthMs=t.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),e.plugins=[o({key:`_faststatsSeqId`}),...this.recordConsole?[a()]:[]],this.stopRecording=s(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 e=this.events;this.events=[];let r=t(),i={token:this.siteKey,sessionId:n(),...r?{identifier:r}:{},sequence:this.sequenceNumber++,timestamp:Date.now(),url:window.location.href,events:e};this.debug&&console.log(`[Replay] Sending ${e.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,r=(e.size/1024).toFixed(1);return i({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 n()}};export{c as default};
@@ -0,0 +1 @@
1
+ const e=`https://metrics.faststats.dev`;function t(e){return e.replace(/\/+$/,``)}function n(n){return n===void 0||n===``?e:t(n)||e}function r(e){return`${n(e)}/v1/web`}function i(e){return`${n(e)}/v1/identify`}function a(e){return`${n(e)}/v1/replay`}function o(e){return`${n(e)}/v1/vitals`}let s=!1;function c(e){s=e}function l(e){return e??s?``:u()}function u(){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 d(e){return e||typeof localStorage>`u`?``:(localStorage.removeItem(`faststats_anon_id`),u())}function f(){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 p(){return typeof sessionStorage>`u`?``:(sessionStorage.removeItem(`session_id`),sessionStorage.removeItem(`session_timestamp`),sessionStorage.removeItem(`session_start`),f())}function m(){typeof sessionStorage>`u`||sessionStorage.getItem(`session_id`)&&sessionStorage.setItem(`session_timestamp`,Date.now().toString())}function h(){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 g(e){return typeof e!=`number`||!Number.isFinite(e)?100:Math.max(0,Math.min(100,e))}export{m as a,c,a as d,r as f,h as i,i as l,l as n,d as o,o as p,f as r,p as s,g as t,n as u};
@@ -0,0 +1 @@
1
+ import{p as e,r as t,t as n}from"./types-8FXsUqbi.js";import{onCLS as r,onFCP as i,onINP as a,onLCP as o,onTTFB as s}from"web-vitals/attribution";var c=class{endpoint;siteKey;debug;samplingPercentage;started=!1;sessionSamplingSeed;metricsMap=new Map;flushed=!1;handleVisibilityChange=()=>{document.visibilityState===`hidden`&&this.finalizeAndFlush()};handlePageHide=()=>{this.finalizeAndFlush()};constructor(t){this.siteKey=t.siteKey,this.endpoint=e(t.baseUrl),this.debug=t.debug??!1,this.samplingPercentage=n(t.samplingPercentage),this.sessionSamplingSeed=Math.random()*100}start(){this.started||typeof window>`u`||(this.started=!0,this.flushed=!1,document.addEventListener(`visibilitychange`,this.handleVisibilityChange),window.addEventListener(`pagehide`,this.handlePageHide),this.debug&&console.log(`[WebVitals] Tracking started`),r(e=>this.captureMetric(e)),a(e=>this.captureMetric(e)),o(e=>this.captureMetric(e)),i(e=>this.captureMetric(e)),s(e=>this.captureMetric(e)))}stop(){!this.started||typeof window>`u`||(this.started=!1,document.removeEventListener(`visibilitychange`,this.handleVisibilityChange),window.removeEventListener(`pagehide`,this.handlePageHide),this.finalizeAndFlush())}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:t(),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{c as default};
package/package.json CHANGED
@@ -19,22 +19,25 @@
19
19
  "publishConfig": {
20
20
  "access": "public"
21
21
  },
22
- "version": "0.1.2",
22
+ "version": "0.1.4",
23
23
  "scripts": {
24
24
  "build": "tsdown",
25
25
  "dev": "bun run build",
26
+ "typecheck": "tsc --noEmit -p tsconfig.json",
27
+ "test": "bun test",
26
28
  "deploy:worker": "wrangler deploy"
27
29
  },
28
30
  "devDependencies": {
29
31
  "@biomejs/biome": "2.4.2",
30
32
  "@rrweb/types": "^2.0.0-alpha.20",
31
33
  "@types/bun": "latest",
32
- "tsdown": "^0.20.3",
34
+ "tsdown": "^0.21.4",
33
35
  "typescript": "^5.9.3",
34
36
  "wrangler": "^4.66.0"
35
37
  },
36
38
  "dependencies": {
37
39
  "@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.20",
40
+ "@rrweb/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.20",
38
41
  "rrweb": "^2.0.0-alpha.4",
39
42
  "rrweb-snapshot": "^2.0.0-alpha.4",
40
43
  "web-vitals": "^5.1.0"