@faststats/web 0.2.7 → 0.2.8
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 +19 -0
- package/dist/chunks/analytics-Cmawx5f9.js +1 -0
- package/dist/chunks/api-urls-BrkcoElX.js +1 -0
- package/dist/{error-asweBGYd.js → chunks/error-1K0dai1m.js} +2 -2
- package/dist/chunks/feature-flags-6rZlmhfu.d.ts +18 -0
- package/dist/chunks/feature-flags-BPRTgvIh.js +1 -0
- package/dist/chunks/replay-CtMtn0C-.js +1 -0
- package/dist/chunks/replay-IhsP2Ab4.d.ts +77 -0
- package/dist/chunks/rolldown-runtime-MP-BAFHD.js +1 -0
- package/dist/chunks/types-C3vW7XGe.js +1 -0
- package/dist/chunks/web-vitals-BooBPWJC.js +1 -0
- package/dist/error.d.ts +44 -0
- package/dist/error.js +1 -0
- package/dist/feature-flags.d.ts +2 -0
- package/dist/feature-flags.js +1 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +1 -0
- package/dist/replay.d.ts +2 -0
- package/dist/replay.js +1 -0
- package/dist/web-vitals.d.ts +27 -0
- package/dist/web-vitals.js +1 -0
- package/package.json +32 -11
- package/scripts/check-bundle-size.mjs +90 -9
- package/src/analytics.ts +47 -41
- package/src/entries/error.ts +6 -0
- package/src/entries/feature-flags.ts +5 -0
- package/src/entries/main.ts +21 -0
- package/src/entries/replay.ts +1 -0
- package/src/entries/web-vitals.ts +1 -0
- package/src/env.d.ts +2 -0
- package/src/error.ts +5 -0
- package/src/replay.ts +104 -59
- package/src/sdk.ts +8 -0
- package/src/utils/types.ts +1 -1
- package/src/web-vitals.ts +32 -14
- package/tests/replay.test.ts +150 -21
- package/tsdown.config.ts +16 -1
- package/dist/analytics-Ct-lghlf.js +0 -1
- package/dist/module.d.ts +0 -531
- package/dist/module.js +0 -1
- package/dist/replay-DUA5c3Jf.js +0 -1
- package/dist/types-Df70G0eM.js +0 -1
- package/dist/web-vitals-gcUR-TX_.js +0 -1
- package/src/module.ts +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @faststats/web
|
|
2
2
|
|
|
3
|
+
## 0.2.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5d0afa5: perf: shrink bundle and improve treeshaking
|
|
8
|
+
|
|
9
|
+
- mark package as side-effect free (`"sideEffects": false`)
|
|
10
|
+
- add subpath exports: `@faststats/web/feature-flags`, `/replay`, `/error`, `/web-vitals`
|
|
11
|
+
- lazy-load `fetchFeatureFlagEvaluation` from `checkFeatureFlag`
|
|
12
|
+
- default web-vitals to the core build; opt into attribution via `webVitals.attribution: true`
|
|
13
|
+
- dynamically import rrweb sequential-id and console plugins (console only when `recordConsole !== false`)
|
|
14
|
+
- import `EventType` from `@rrweb/types` instead of the rrweb barrel so consumer bundlers can prune the playback engine, packer, and replay-side console plugin
|
|
15
|
+
- move `@rrweb/types` from devDependencies to dependencies (now a runtime import)
|
|
16
|
+
- main entry filename is now `dist/index.js` (was `dist/module.js`); the public entry no longer re-exports `featureFlagsCheckUrl` / `normalizeFeatureFlagsBaseUrl`
|
|
17
|
+
- per-entry raw + gzip budgets enforced by `scripts/check-bundle-size.mjs`
|
|
18
|
+
|
|
19
|
+
- cdf521f: chore: report sdk name and version for error tracking
|
|
20
|
+
- a844fed: fix: enable compression for replays by default
|
|
21
|
+
|
|
3
22
|
## 0.2.7
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{i as e,n as t,o as n,r}from"./api-urls-BrkcoElX.js";import{a as i,c as a,i as o,n as s,o as c,r as l,s as u,t as d}from"./types-C3vW7XGe.js";function f(e){return e.replayOptions?.samplingPercentage!==void 0||e.sessionReplays?.sampling?.percentage!==void 0}function p(e,t,n){if(!(e.sessionReplays?.enabled??e.trackReplay??f(e)))return null;let r=e.replayOptions??{};return{siteKey:e.siteKey,baseUrl:t,debug:n,...r,samplingPercentage:d(r.samplingPercentage??e.sessionReplays?.sampling?.percentage)}}const m={instance:null,pendingConsentMode:void 0};function h(){return m.instance}function g(e,t){typeof window>`u`||C()||m.instance?.track(e,t??{})}function _(e,t,n){typeof window>`u`||C()||m.instance?.identify(e,t,n??{})}function v(e=!0){typeof window>`u`||C()||m.instance?.logout(e)}function y(e){if(m.instance){m.instance.setConsentMode(e);return}m.pendingConsentMode=e}function b(){y(`granted`)}function x(){y(`denied`)}function S(e){typeof window>`u`||C()||m.instance?.reportError(e)}function C(){if(typeof localStorage>`u`)return!1;let e=localStorage.getItem(`disable-faststats`);return e===`true`||e===`1`}async function w(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`,useBeacon:s=!0,keepalive:c=!0}=e;if(s&&typeof navigator<`u`&&typeof navigator.sendBeacon==`function`)try{let e=n instanceof Blob?n:typeof Blob<`u`?new Blob([n],{type:r}):n;if(navigator.sendBeacon(t,e))return a&&console.log(`${o} Sent via beacon`),!0}catch{}if(typeof fetch!=`function`)return a&&console.warn(`${o} Failed to send`),!1;try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:c}),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 T(e){for(;e;){if(e instanceof HTMLAnchorElement&&e.href)return e;e=e.parentNode}return null}function E(){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 D=class{webEndpoint;baseUrl;featureFlagsBaseUrl;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=[];errorTracker=null;handleVisibilityChange=()=>{document.visibilityState===`hidden`?(this.leavePage(),this.stopHeartbeat()):this.startHeartbeat()};handlePageHide=()=>{this.leavePage()};handlePopState=()=>{this.navigate()};handleHashChange=()=>{this.navigate()};constructor(t){this.options=t,this.baseUrl=r(t.baseUrl),this.featureFlagsBaseUrl=e(t.featureFlagsBaseUrl),this.webEndpoint=n(this.baseUrl),this.debug=t.debug??!1,this.consentMode=t.consent?.mode??`granted`,this.cookielessWhilePending=t.consent?.cookielessWhilePending??!0,m.pendingConsentMode!==void 0&&(this.consentMode=m.pendingConsentMode,m.pendingConsentMode=void 0),(t.autoTrack??!0)&&this.init()}log(e){this.debug&&console.log(`[Analytics] ${e}`)}init(){if(!(typeof window>`u`)){if(C()){this.log(`disabled`);return}m.instance=this,a(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||C()?!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?.();this.errorTracker=null}registerChildTracker(e){return!this.started||this.destroyed||m.instance!==this?(e.stop?.(),!1):(this.childTrackers.push(e),!0)}async startErrorTracker(){try{let{default:e}=await import(`./error-1K0dai1m.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,sdkName:this.options.sdkName,sdkVersion:this.options.sdkVersion});if(t.start(),!this.registerChildTracker(t))return;for(this.errorTracker=t;this.pendingReportedErrors.length>0;){let e=this.pendingReportedErrors.shift();e&&t.captureError(e)}this.log(`error loaded`)}catch(e){this.log(`failed to initialize error tracker: ${String(e)}`)}}async startWebVitalsTracker(){try{let{default:e}=await import(`./web-vitals-BooBPWJC.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let t=new e({siteKey:this.options.siteKey,baseUrl:this.baseUrl,debug:this.debug,samplingPercentage:d(this.options.webVitals?.sampling?.percentage),attribution:this.options.webVitals?.attribution??!1});if(t.start(),!this.registerChildTracker(t))return;this.log(`web-vitals loaded`)}catch(e){this.log(`failed to initialize web-vitals tracker: ${String(e)}`)}}async startReplayTracker(e){try{let{default:t}=await import(`./replay-CtMtn0C-.js`).then(e=>e.n);if(!this.started||this.destroyed||m.instance!==this)return;let n=new t(e);if(n.start(),!this.registerChildTracker(n))return;this.log(`replay loaded`)}catch(e){this.log(`failed to initialize replay tracker: ${String(e)}`)}}async start(){if(this.started||this.destroyed||typeof window>`u`)return;if(m.instance&&m.instance!==this){this.log(`already started by another instance`);return}if(C()){this.log(`disabled`);return}this.started=!0,m.instance=this,a(this.isCookielessMode()),l();let e=this.options,t=p(e,this.baseUrl,this.debug);t&&this.startReplayTracker(t),e.trackErrors&&this.startErrorTracker(),e.trackWebVitals&&this.startWebVitalsTracker(),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(),m.instance===this&&(m.instance=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,n,r={}){if(!this.ensureStarted()||this.isCookielessMode())return;let i=e.trim(),a=n.trim();!i||!a||w({url:t(this.baseUrl),data:JSON.stringify({token:this.options.siteKey,identifier:s(!1),externalId:i,email:a,name:r.name?.trim()||void 0,phone:r.phone?.trim()||void 0,avatarUrl:r.avatarUrl?.trim()||void 0,traits:r.traits??{}}),contentType:`text/plain`,debug:this.debug,debugPrefix:`[Analytics] identify`})}logout(e=!0){this.ensureStarted()&&(e&&c(this.isCookielessMode()),u())}setConsentMode(e){this.consentMode=e,a(this.isCookielessMode())}optIn(){this.setConsentMode(`granted`)}optOut(){this.setConsentMode(`denied`)}getConsentMode(){return this.consentMode}getAnonymousId(){return s(this.isCookielessMode())}getSessionId(){return l()}async checkFeatureFlag(e,t,n){if(typeof window>`u`||C())return{value:`false`};let r=n?.externalId?.trim(),i=r?void 0:this.getAnonymousId();if(!r&&!i)return{value:`false`};let{fetchFeatureFlagEvaluation:a}=await import(`./feature-flags-BPRTgvIh.js`).then(e=>e.t);return a(e,{baseUrl:this.featureFlagsBaseUrl,projectToken:this.options.siteKey,...r?{externalId:r}:{identifier:i},attributes:t,signal:n?.signal})}reportError(e){if(this.destroyed||typeof window>`u`||C()||!(this.options.trackErrors??!1)||!this.ensureStarted())return;let t=this.errorTracker;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||C())return;let n=s(this.isCookielessMode()),r=JSON.stringify({token:this.options.siteKey,...n?{userId:n}:{},sessionId:l(),data:{event:e,page:location.pathname,referrer:document.referrer||null,title:document.title||``,url:location.href,...E(),...t}});this.log(e),w({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-o()})}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}i()},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=T(e.target);t&&t.host!==location.host&&this.track(`outbound_link`,{outbound_link:t.href})};this.addDocumentListener(`click`,e),this.addDocumentListener(`auxclick`,e)}};export{v as a,S as c,g as d,C as i,w as l,h as n,b as o,_ as r,x as s,D as t,y as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`https://metrics.faststats.dev`,t=`https://flags.faststats.dev`;function n(e){return e.replace(/\/+$/,``)}function r(t){return t===void 0||t===``?e:n(t)||e}function i(e){return`${r(e)}/v1/web`}function a(e){return`${r(e)}/v1/identify`}function o(e){return`${r(e)}/v1/replay`}function s(e){return`${r(e)}/v1/vitals`}function c(e){return e===void 0||e===``?t:n(e)||t}function l(e){return`${c(e)}/v1/check`}export{o as a,c as i,a as n,i as o,r,s,l as t};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`).map(e=>e.trim()).filter(Boolean);function
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{o as t}from"./api-urls-BrkcoElX.js";import{n,r}from"./types-C3vW7XGe.js";import{l as i}from"./analytics-Cmawx5f9.js";var a=e({default:()=>d});const o=/(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//,s=e=>e?.split(`
|
|
2
|
+
`).map(e=>e.trim()).filter(Boolean);function c(e){if(e instanceof Error)return{error:e.name?.trim()||`Error`,message:e.message,stack:s(e.stack),cause:c(e.cause)};if(typeof e==`string`)return{error:`Error`,message:e}}function l(e){return e?`${e.error}\0${e.message??``}\0${l(e.cause)}`:``}function u(e){let t=[e.kind,e.handled?`handled`:`unhandled`,e.message,e.filename??``,e.lineno??``,l(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 d=class{endpoint;handled=new WeakSet;queue=new Map;timer=null;started=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl)}get debug(){return this.options.debug??!1}get flushInterval(){return this.options.flushInterval??5e3}get maxQueueSize(){return this.options.maxQueueSize??50}log(...e){this.debug&&console.log(`[ErrorTracker]`,...e)}start(){this.started||typeof window>`u`||(this.started=!0,window.addEventListener(`error`,this.onError),window.addEventListener(`unhandledrejection`,this.onRejection),document.addEventListener(`visibilitychange`,this.onVisibilityChange),window.addEventListener(`pagehide`,this.flush),this.timer=setInterval(this.flush,this.flushInterval),this.log(`Started listening for errors`))}stop(){!this.started||typeof window>`u`||(this.started=!1,window.removeEventListener(`error`,this.onError),window.removeEventListener(`unhandledrejection`,this.onRejection),document.removeEventListener(`visibilitychange`,this.onVisibilityChange),window.removeEventListener(`pagehide`,this.flush),this.timer&&clearInterval(this.timer),this.timer=null,this.flush(),this.log(`Stopped listening for errors`))}captureError(e){this.record({kind:`error`,message:e.message,stack:e.stack,handled:!0,cause:c(e.cause)},e)}onError=e=>{let t=e.error;this.record({kind:`error`,message:e.message||`Unknown error`,filename:e.filename||void 0,lineno:e.lineno||void 0,stack:t instanceof Error?t.stack:void 0,handled:!1,cause:t instanceof Error?c(t.cause):void 0},t)};onRejection=e=>{let t=e.reason;this.record({kind:`unhandledrejection`,message:t.message||`Unhandled promise rejection`,stack:t.stack||void 0,handled:!1,cause:c(t.cause)},t)};onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush()};record(e,t){if(t instanceof Error){if(this.handled.has(t)){this.log(`Skipping duplicate:`,t.message);return}this.handled.add(t)}if(o.test(`${e.filename??``}\n${e.stack??``}`))return;let n=u(e),r=this.queue.get(n);r?r.count+=1:this.queue.set(n,{hash:n,count:1,error:e.kind===`unhandledrejection`?`UnhandledRejection`:`Error`,message:e.message,stack:s(e.stack),handled:e.handled,...e.cause?{cause:e.cause}:{}}),this.log(`Captured error:`,e),this.queue.size>=this.maxQueueSize&&this.flush()}flush=()=>{if(this.queue.size===0)return;let e=[...this.queue.values()];this.queue.clear();let t=n(),a=globalThis.__SOURCEMAPS_BUILD__,o=typeof a?.buildId==`string`&&a.buildId.trim()?a.buildId:void 0,s=JSON.stringify({token:this.options.siteKey,...t?{userId:t}:{},sessionId:r(),...o?{buildId:o}:{},sdkName:this.options.sdkName??`@faststats/web`,sdkVersion:this.options.sdkVersion??`0.2.8`,data:{url:location.href,page:location.pathname,referrer:document.referrer||null,title:document.title},errors:e});this.log(`Flushing errors:`,e),this.log(`Payload:`,s),i({url:this.endpoint,data:s,debug:this.debug,debugPrefix:`[ErrorTracker]`})}};export{a as n,d as t};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/feature-flags.d.ts
|
|
2
|
+
type FeatureFlagEvaluation = {
|
|
3
|
+
value: string;
|
|
4
|
+
variantKey?: string;
|
|
5
|
+
variantValue?: string;
|
|
6
|
+
};
|
|
7
|
+
type FeatureFlagCheckContext = {
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
projectToken?: string;
|
|
10
|
+
projectId?: string;
|
|
11
|
+
identifier?: string;
|
|
12
|
+
externalId?: string;
|
|
13
|
+
attributes?: Record<string, unknown>;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
};
|
|
16
|
+
declare function fetchFeatureFlagEvaluation(flagKey: string, context: FeatureFlagCheckContext): Promise<FeatureFlagEvaluation>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { FeatureFlagEvaluation as n, fetchFeatureFlagEvaluation as r, FeatureFlagCheckContext as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{t}from"./api-urls-BrkcoElX.js";var n=e({fetchFeatureFlagEvaluation:()=>r});async function r(e,n){if(n.projectToken&&n.projectId)throw Error(`provide either projectToken or projectId, not both`);if(n.identifier&&n.externalId)throw Error(`provide either serverId or externalId, not both`);let r=n.projectToken,i=n.projectId,a=n.identifier,o=n.externalId;if(!r&&!i)throw Error(`feature flag check requires projectToken or projectId`);if(!a&&!o)throw Error(`feature flag check requires serverId or externalId`);let s={key:e,...i?{projectId:i}:{},...a?{identifier:a}:{externalId:o}};n.attributes&&Object.keys(n.attributes).length>0&&(s.attributes=n.attributes);let c={"Content-Type":`application/json`};r&&(c.Authorization=`Bearer ${r}`);let l=t(n.baseUrl),u=await fetch(l,{method:`POST`,headers:c,body:JSON.stringify(s),credentials:`omit`,signal:n.signal});if(!u.ok){let e=``;try{let t=await u.json();typeof t.error==`string`&&t.error.length>0&&(e=` — ${t.error}`)}catch{}throw Error(`feature flag check failed: HTTP ${u.status}${e}`)}return await u.json()}export{r as n,n as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{a as t}from"./api-urls-BrkcoElX.js";import{i as n,n as r,r as i,t as a}from"./types-C3vW7XGe.js";import{l as o}from"./analytics-Cmawx5f9.js";import{EventType as s}from"@rrweb/types";import{record as c}from"rrweb";var l=e({default:()=>f});const u={mousemove:50,mouseInteraction:!0,scroll:150,media:800,input:`last`},d={script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0};var f=class{endpoint;compressionSupported=typeof window<`u`&&`CompressionStream`in window;sampled;events=[];pending=[];pendingSizeBytes=0;sessionId;started=!1;startTime=0;sequence=0;intervalId=null;flushTask=null;retryTask=null;minLengthFlushTask=null;stopRecording;sending=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl),this.sampled=Math.random()*100<a(e.samplingPercentage)}get debug(){return this.options.debug??!1}get flushInterval(){return this.options.flushInterval??1e4}get maxEvents(){return this.options.maxEvents??500}get maxPendingBatches(){return this.options.maxPendingBatches??30}get maxQueueSizeBytes(){return this.options.maxQueueSizeBytes??2097152}get minReplayLengthMs(){return this.options.minReplayLengthMs??3e3}get shouldCompress(){return this.options.compress??!0}log(...e){this.debug&&console.log(`[Replay]`,...e)}start(){this.started||typeof window>`u`||!this.sampled||(this.started=!0,this.sessionId=i(),this.startTime=n(),this.beginRecording(),this.intervalId=setInterval(this.requestFlush,this.flushInterval),window.addEventListener(`beforeunload`,this.onUnload),window.addEventListener(`pagehide`,this.onUnload),document.addEventListener(`visibilitychange`,this.onVisibilityChange),this.log(`Recording started`))}async beginRecording(){let e=this.options.recordConsole??!0,[{getRecordSequentialIdPlugin:t},n]=await Promise.all([import(`@rrweb/rrweb-plugin-sequential-id-record`),e?import(`@rrweb/rrweb-plugin-console-record`):Promise.resolve(null)]);if(!this.started)return;let r=[t({key:`_faststatsSeqId`})];n&&r.push(n.getRecordConsolePlugin()),this.stopRecording=c({emit:this.onEvent,sampling:this.options.sampling??u,slimDOMOptions:this.options.slimDOMOptions??d,maskAllInputs:this.options.maskAllInputs??!0,maskInputOptions:this.options.maskInputOptions??{password:!0,email:!0,tel:!0},blockClass:this.options.blockClass,blockSelector:this.options.blockSelector,maskTextClass:this.options.maskTextClass,maskTextSelector:this.options.maskTextSelector,checkoutEveryNms:this.options.checkoutEveryNms??6e4,checkoutEveryNth:this.options.checkoutEveryNth,plugins:r})}stop(){if(this.started){if(this.started=!1,this.stopRecording?.(),this.stopRecording=void 0,this.intervalId&&clearInterval(this.intervalId),this.flushTask&&clearTimeout(this.flushTask),this.retryTask&&clearTimeout(this.retryTask),this.minLengthFlushTask&&clearTimeout(this.minLengthFlushTask),this.intervalId=null,this.flushTask=null,this.retryTask=null,this.minLengthFlushTask=null,window.removeEventListener(`beforeunload`,this.onUnload),window.removeEventListener(`pagehide`,this.onUnload),document.removeEventListener(`visibilitychange`,this.onVisibilityChange),!this.hasReachedMinLength()){this.events.length=0,this.sessionId=void 0,this.log(`Session too short (${Date.now()-this.startTime}ms), discarding events`);return}this.flush(!0),this.sessionId=void 0,this.log(`Recording stopped`)}}getSessionId(){return this.sessionId??i()}onEvent=(e,t)=>{if(this.events.push(e),t||this.events.length>=this.maxEvents||e.type===s.FullSnapshot&&this.hasReachedMinLength()){this.requestFlush();return}this.scheduleMinLengthFlush()};onUnload=()=>{this.flush(!0)};onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush(!0)};hasReachedMinLength(){return this.minReplayLengthMs<=0||Date.now()-this.startTime>=this.minReplayLengthMs}requestFlush=()=>{this.flushTask||this.sending||this.events.length===0||(this.minLengthFlushTask&&=(clearTimeout(this.minLengthFlushTask),null),this.flushTask=setTimeout(()=>{this.flushTask=null,this.flush(!1)},0))};scheduleMinLengthFlush(){if(this.minLengthFlushTask||this.events.length===0||this.hasReachedMinLength())return;let e=Math.max(0,this.minReplayLengthMs-(Date.now()-this.startTime)),t=Math.min(e,2147483647);this.minLengthFlushTask=setTimeout(()=>{this.minLengthFlushTask=null,this.requestFlush()},t)}createBatch(e){let t=r();return{token:this.options.siteKey,sessionId:this.getSessionId(),...t?{identifier:t}:{},sequence:this.sequence++,timestamp:Date.now(),url:window.location.href,events:e}}getBatchSizeBytes(e){return new TextEncoder().encode(JSON.stringify(e)).byteLength}dropOldestPendingBatch(e){let t=this.pending.shift();t&&(this.pendingSizeBytes=Math.max(0,this.pendingSizeBytes-this.getBatchSizeBytes(t)),this.log(`${e}, dropping batch ${t.sequence}`))}enqueueBatch(e){let t=this.getBatchSizeBytes(e);if(t>this.maxQueueSizeBytes){this.log(`Replay batch ${e.sequence} is ${t}B, exceeding ${this.maxQueueSizeBytes}B queue limit; dropping`);return}for(;this.pending.length>0&&this.pendingSizeBytes+t>this.maxQueueSizeBytes;)this.dropOldestPendingBatch(`Pending queue size limit reached`);for(;this.pending.length>=this.maxPendingBatches;)this.dropOldestPendingBatch(`Pending batch limit reached`);this.pending.push(e),this.pendingSizeBytes+=t}async encodeBatch(e){let t=JSON.stringify(e);if(!this.shouldCompress||!this.compressionSupported)return{data:t,isCompressed:!1};try{let e=await this.compress(t);return this.log(`Compressed ${t.length}B -> ${e.byteLength}B (${Math.round(e.byteLength/t.length*100)}%)`),{data:e,isCompressed:!0}}catch{return this.log(`Compression failed, using uncompressed`),{data:t,isCompressed:!1}}}async flush(e){if(!this.sending){if(this.events.length>0)if(!this.hasReachedMinLength())this.scheduleMinLengthFlush();else{let e=this.createBatch(this.events.splice(0));this.enqueueBatch(e)}if(this.pending.length!==0){this.sending=!0;try{for(;this.pending.length>0;){let t=this.pending[0];if(!t)break;let n=await this.encodeBatch(t);if(!await this.send(n.data,n.isCompressed,e)){this.log(`Failed to send replay batch ${t.sequence}, retrying`),this.scheduleRetry();break}this.dropOldestPendingBatch(`Sent replay batch`),e=!1}}finally{this.sending=!1}}}}scheduleRetry(){this.retryTask||=setTimeout(()=>{this.retryTask=null,this.flush(!1)},1e3)}async compress(e){let t=new Blob([e]).stream().pipeThrough(new CompressionStream(`gzip`)),n=await new Response(t).arrayBuffer();return new Uint8Array(n)}send(e,t,n){return o({url:t?`${this.endpoint}?encoding=gzip`:this.endpoint,data:e,contentType:t?`application/octet-stream`:`application/json`,debug:!1,useBeacon:!1,keepalive:n})??Promise.resolve(!1)}};export{l as n,f as t};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { eventWithTime } from "@rrweb/types";
|
|
2
|
+
import { recordOptions } from "rrweb/typings/types";
|
|
3
|
+
import { SlimDOMOptions } from "rrweb-snapshot";
|
|
4
|
+
|
|
5
|
+
//#region src/replay.d.ts
|
|
6
|
+
interface ReplayTrackerOptions {
|
|
7
|
+
siteKey: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
compress?: boolean;
|
|
11
|
+
samplingPercentage?: number;
|
|
12
|
+
flushInterval?: number;
|
|
13
|
+
maxEvents?: number;
|
|
14
|
+
maxPendingBatches?: number;
|
|
15
|
+
maxQueueSizeBytes?: number;
|
|
16
|
+
minReplayLengthMs?: number;
|
|
17
|
+
sampling?: recordOptions<eventWithTime>["sampling"];
|
|
18
|
+
slimDOMOptions?: SlimDOMOptions;
|
|
19
|
+
maskAllInputs?: boolean;
|
|
20
|
+
maskInputOptions?: recordOptions<eventWithTime>["maskInputOptions"];
|
|
21
|
+
blockClass?: string;
|
|
22
|
+
blockSelector?: string;
|
|
23
|
+
maskTextClass?: string;
|
|
24
|
+
maskTextSelector?: string;
|
|
25
|
+
checkoutEveryNms?: number;
|
|
26
|
+
checkoutEveryNth?: number;
|
|
27
|
+
recordConsole?: boolean;
|
|
28
|
+
}
|
|
29
|
+
declare class ReplayTracker {
|
|
30
|
+
private readonly options;
|
|
31
|
+
private readonly endpoint;
|
|
32
|
+
private readonly compressionSupported;
|
|
33
|
+
private readonly sampled;
|
|
34
|
+
private readonly events;
|
|
35
|
+
private readonly pending;
|
|
36
|
+
private pendingSizeBytes;
|
|
37
|
+
private sessionId;
|
|
38
|
+
private started;
|
|
39
|
+
private startTime;
|
|
40
|
+
private sequence;
|
|
41
|
+
private intervalId;
|
|
42
|
+
private flushTask;
|
|
43
|
+
private retryTask;
|
|
44
|
+
private minLengthFlushTask;
|
|
45
|
+
private stopRecording?;
|
|
46
|
+
private sending;
|
|
47
|
+
constructor(options: ReplayTrackerOptions);
|
|
48
|
+
private get debug();
|
|
49
|
+
private get flushInterval();
|
|
50
|
+
private get maxEvents();
|
|
51
|
+
private get maxPendingBatches();
|
|
52
|
+
private get maxQueueSizeBytes();
|
|
53
|
+
private get minReplayLengthMs();
|
|
54
|
+
private get shouldCompress();
|
|
55
|
+
private log;
|
|
56
|
+
start(): void;
|
|
57
|
+
private beginRecording;
|
|
58
|
+
stop(): void;
|
|
59
|
+
getSessionId(): string | undefined;
|
|
60
|
+
private onEvent;
|
|
61
|
+
private onUnload;
|
|
62
|
+
private onVisibilityChange;
|
|
63
|
+
private hasReachedMinLength;
|
|
64
|
+
private requestFlush;
|
|
65
|
+
private scheduleMinLengthFlush;
|
|
66
|
+
private createBatch;
|
|
67
|
+
private getBatchSizeBytes;
|
|
68
|
+
private dropOldestPendingBatch;
|
|
69
|
+
private enqueueBatch;
|
|
70
|
+
private encodeBatch;
|
|
71
|
+
private flush;
|
|
72
|
+
private scheduleRetry;
|
|
73
|
+
private compress;
|
|
74
|
+
private send;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { ReplayTrackerOptions as n, ReplayTracker as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t};
|
|
@@ -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{t as e}from"./rolldown-runtime-MP-BAFHD.js";import{s as t}from"./api-urls-BrkcoElX.js";import{r as n,t as r}from"./types-C3vW7XGe.js";var i=e({default:()=>o});async function a(e){if(e){let{onCLS:e,onFCP:t,onINP:n,onLCP:r,onTTFB:i}=await import(`web-vitals/attribution`);return[e,t,n,r,i]}let{onCLS:t,onFCP:n,onINP:r,onLCP:i,onTTFB:a}=await import(`web-vitals`);return[t,n,r,i,a]}var o=class{endpoint;metrics=new Map;sampled;started=!1;flushed=!1;constructor(e){this.options=e,this.endpoint=t(e.baseUrl),this.sampled=Math.random()*100<r(e.samplingPercentage)}get debug(){return this.options.debug??!1}log(...e){this.debug&&console.log(`[WebVitals]`,...e)}start(){this.started||typeof window>`u`||(this.started=!0,this.flushed=!1,document.addEventListener(`visibilitychange`,this.onVisibilityChange),window.addEventListener(`pagehide`,this.flush),this.log(`Tracking started`),this.observe())}stop(){!this.started||typeof window>`u`||(this.started=!1,document.removeEventListener(`visibilitychange`,this.onVisibilityChange),window.removeEventListener(`pagehide`,this.flush),this.flush())}async observe(){try{let e=await a(this.options.attribution??!1);if(!this.started)return;for(let t of e)t(this.captureMetric)}catch(e){this.log(`Failed to load web-vitals`,e)}}onVisibilityChange=()=>{document.visibilityState===`hidden`&&this.flush()};captureMetric=e=>{if(this.flushed||!this.sampled)return;let t=e.name,n=e.attribution??void 0;this.metrics.set(t,{value:e.value,attributes:{id:e.id,rating:e.rating,delta:e.delta,navigationType:e.navigationType,...n??{}}}),this.log(`${t} captured: ${e.value}`)};flush=()=>{if(this.flushed||this.metrics.size===0)return;this.flushed=!0;let e=[...this.metrics.entries()].map(([e,t])=>({metric:e,value:t.value,attributes:t.attributes})),t=e.map(e=>e.metric).join(`, `);this.metrics.clear();let r=JSON.stringify({sessionId:n(),vitals:e,metadata:{url:window.location.href}});this.log(`Sending final metrics (${e.length}): ${t}`),typeof fetch==`function`&&fetch(this.endpoint,{method:`POST`,body:r,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${this.options.siteKey}`},keepalive:!0}).catch(()=>{this.log(`Failed to send metrics`)})}};export{i as n,o as t};
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/error.d.ts
|
|
2
|
+
type ErrorEntry = {
|
|
3
|
+
error: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
stack?: string[];
|
|
6
|
+
handled?: boolean;
|
|
7
|
+
cause?: ErrorEntry;
|
|
8
|
+
};
|
|
9
|
+
type ErrorTracking = ErrorEntry & {
|
|
10
|
+
hash: string;
|
|
11
|
+
count: number;
|
|
12
|
+
};
|
|
13
|
+
interface ErrorTrackingOptions {
|
|
14
|
+
siteKey: string;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
flushInterval?: number;
|
|
18
|
+
maxQueueSize?: number;
|
|
19
|
+
sdkName?: string;
|
|
20
|
+
sdkVersion?: string;
|
|
21
|
+
}
|
|
22
|
+
declare class ErrorTracker {
|
|
23
|
+
private readonly options;
|
|
24
|
+
private readonly endpoint;
|
|
25
|
+
private readonly handled;
|
|
26
|
+
private readonly queue;
|
|
27
|
+
private timer;
|
|
28
|
+
private started;
|
|
29
|
+
constructor(options: ErrorTrackingOptions);
|
|
30
|
+
private get debug();
|
|
31
|
+
private get flushInterval();
|
|
32
|
+
private get maxQueueSize();
|
|
33
|
+
private log;
|
|
34
|
+
start(): void;
|
|
35
|
+
stop(): void;
|
|
36
|
+
captureError(error: Error): void;
|
|
37
|
+
private onError;
|
|
38
|
+
private onRejection;
|
|
39
|
+
private onVisibilityChange;
|
|
40
|
+
private record;
|
|
41
|
+
private flush;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { type ErrorEntry, type ErrorTracking, type ErrorTrackingOptions, ErrorTracker as default };
|
package/dist/error.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunks/error-1K0dai1m.js";export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./chunks/feature-flags-BPRTgvIh.js";export{e as fetchFeatureFlagEvaluation};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { n as FeatureFlagEvaluation, r as fetchFeatureFlagEvaluation, t as FeatureFlagCheckContext } from "./chunks/feature-flags-6rZlmhfu.js";
|
|
2
|
+
import { n as ReplayTrackerOptions } from "./chunks/replay-IhsP2Ab4.js";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/types.d.ts
|
|
5
|
+
interface SendDataOptions {
|
|
6
|
+
url: string;
|
|
7
|
+
data: string | Blob | Uint8Array;
|
|
8
|
+
contentType?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
debug?: boolean;
|
|
11
|
+
debugPrefix?: string;
|
|
12
|
+
useBeacon?: boolean;
|
|
13
|
+
keepalive?: boolean;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/analytics.d.ts
|
|
17
|
+
type Dict = Record<string, unknown>;
|
|
18
|
+
interface SamplingOptions {
|
|
19
|
+
percentage?: number;
|
|
20
|
+
}
|
|
21
|
+
interface WebVitalsConfig {
|
|
22
|
+
sampling?: SamplingOptions;
|
|
23
|
+
attribution?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface SessionReplayConfig {
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
sampling?: SamplingOptions;
|
|
28
|
+
}
|
|
29
|
+
type ConsentMode = "pending" | "granted" | "denied";
|
|
30
|
+
interface ConsentConfig {
|
|
31
|
+
mode?: ConsentMode;
|
|
32
|
+
cookielessWhilePending?: boolean;
|
|
33
|
+
}
|
|
34
|
+
interface IdentifyOptions {
|
|
35
|
+
name?: string;
|
|
36
|
+
phone?: string;
|
|
37
|
+
avatarUrl?: string;
|
|
38
|
+
traits?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
interface WebAnalyticsOptions {
|
|
41
|
+
siteKey: string;
|
|
42
|
+
baseUrl?: string;
|
|
43
|
+
featureFlagsBaseUrl?: string;
|
|
44
|
+
debug?: boolean;
|
|
45
|
+
autoTrack?: boolean;
|
|
46
|
+
trackHash?: boolean;
|
|
47
|
+
trackErrors?: boolean;
|
|
48
|
+
trackWebVitals?: boolean;
|
|
49
|
+
trackReplay?: boolean;
|
|
50
|
+
cookieless?: boolean;
|
|
51
|
+
consent?: ConsentConfig;
|
|
52
|
+
webVitals?: WebVitalsConfig;
|
|
53
|
+
sessionReplays?: SessionReplayConfig;
|
|
54
|
+
replayOptions?: Partial<ReplayTrackerOptions>;
|
|
55
|
+
/** @internal */
|
|
56
|
+
sdkName?: string;
|
|
57
|
+
/** @internal */
|
|
58
|
+
sdkVersion?: string;
|
|
59
|
+
}
|
|
60
|
+
declare function getInstance(): WebAnalytics | null;
|
|
61
|
+
declare function trackEvent(eventName: string, properties?: Record<string, unknown>): void;
|
|
62
|
+
declare function identify(externalId: string, email: string, options?: IdentifyOptions): void;
|
|
63
|
+
declare function logout(resetAnonymousIdentity?: boolean): void;
|
|
64
|
+
declare function setConsentMode(mode: ConsentMode): void;
|
|
65
|
+
declare function optIn(): void;
|
|
66
|
+
declare function optOut(): void;
|
|
67
|
+
declare function reportError(error: Error): void;
|
|
68
|
+
declare function isTrackingDisabled(): boolean;
|
|
69
|
+
declare function sendData(options: SendDataOptions): Promise<boolean>;
|
|
70
|
+
declare class WebAnalytics {
|
|
71
|
+
private readonly options;
|
|
72
|
+
private readonly webEndpoint;
|
|
73
|
+
private readonly baseUrl;
|
|
74
|
+
private readonly featureFlagsBaseUrl;
|
|
75
|
+
private readonly debug;
|
|
76
|
+
private started;
|
|
77
|
+
private destroyed;
|
|
78
|
+
private pageKey;
|
|
79
|
+
private navTimer;
|
|
80
|
+
private heartbeatTimer;
|
|
81
|
+
private scrollDepth;
|
|
82
|
+
private pageEntryTime;
|
|
83
|
+
private pagePath;
|
|
84
|
+
private pageUrl;
|
|
85
|
+
private pageHash;
|
|
86
|
+
private hasLeftCurrentPage;
|
|
87
|
+
private scrollHandler;
|
|
88
|
+
private consentMode;
|
|
89
|
+
private readonly cookielessWhilePending;
|
|
90
|
+
private readonly cleanupCallbacks;
|
|
91
|
+
private readonly childTrackers;
|
|
92
|
+
private readonly pendingReportedErrors;
|
|
93
|
+
private errorTracker;
|
|
94
|
+
private readonly handleVisibilityChange;
|
|
95
|
+
private readonly handlePageHide;
|
|
96
|
+
private readonly handlePopState;
|
|
97
|
+
private readonly handleHashChange;
|
|
98
|
+
constructor(options: WebAnalyticsOptions);
|
|
99
|
+
private log;
|
|
100
|
+
private init;
|
|
101
|
+
private registerCleanup;
|
|
102
|
+
private addWindowListener;
|
|
103
|
+
private addDocumentListener;
|
|
104
|
+
private patchHistory;
|
|
105
|
+
private ensureStarted;
|
|
106
|
+
private stopHeartbeat;
|
|
107
|
+
private stopNavigationTimer;
|
|
108
|
+
private stopChildTrackers;
|
|
109
|
+
private registerChildTracker;
|
|
110
|
+
private startErrorTracker;
|
|
111
|
+
private startWebVitalsTracker;
|
|
112
|
+
private startReplayTracker;
|
|
113
|
+
start(): Promise<void>;
|
|
114
|
+
destroy(): void;
|
|
115
|
+
pageview(extra?: Dict): void;
|
|
116
|
+
track(name: string, extra?: Dict): void;
|
|
117
|
+
identify(externalId: string, email: string, options?: IdentifyOptions): void;
|
|
118
|
+
logout(resetAnonymousIdentity?: boolean): void;
|
|
119
|
+
setConsentMode(mode: ConsentMode): void;
|
|
120
|
+
optIn(): void;
|
|
121
|
+
optOut(): void;
|
|
122
|
+
getConsentMode(): ConsentMode;
|
|
123
|
+
getAnonymousId(): string;
|
|
124
|
+
getSessionId(): string;
|
|
125
|
+
checkFeatureFlag(key: string, attributes?: Record<string, unknown>, opts?: {
|
|
126
|
+
externalId?: string;
|
|
127
|
+
signal?: AbortSignal;
|
|
128
|
+
}): Promise<FeatureFlagEvaluation>;
|
|
129
|
+
reportError(error: Error): void;
|
|
130
|
+
private isCookielessMode;
|
|
131
|
+
private send;
|
|
132
|
+
private enterPage;
|
|
133
|
+
private leavePage;
|
|
134
|
+
private trackScroll;
|
|
135
|
+
private startHeartbeat;
|
|
136
|
+
private navigate;
|
|
137
|
+
private links;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { type ConsentMode, type FeatureFlagCheckContext, type FeatureFlagEvaluation, type IdentifyOptions, WebAnalytics, type WebAnalyticsOptions, fetchFeatureFlagEvaluation, getInstance, identify, isTrackingDisabled, logout, optIn, optOut, reportError, sendData, setConsentMode, trackEvent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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"./chunks/analytics-Cmawx5f9.js";import{n as d}from"./chunks/feature-flags-BPRTgvIh.js";export{l as WebAnalytics,d as fetchFeatureFlagEvaluation,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};
|
package/dist/replay.d.ts
ADDED
package/dist/replay.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunks/replay-CtMtn0C-.js";export{e as default};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/web-vitals.d.ts
|
|
2
|
+
interface WebVitalsOptions {
|
|
3
|
+
siteKey: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
debug?: boolean;
|
|
6
|
+
samplingPercentage?: number;
|
|
7
|
+
attribution?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare class WebVitalsTracker {
|
|
10
|
+
private readonly options;
|
|
11
|
+
private readonly endpoint;
|
|
12
|
+
private readonly metrics;
|
|
13
|
+
private readonly sampled;
|
|
14
|
+
private started;
|
|
15
|
+
private flushed;
|
|
16
|
+
constructor(options: WebVitalsOptions);
|
|
17
|
+
private get debug();
|
|
18
|
+
private log;
|
|
19
|
+
start(): void;
|
|
20
|
+
stop(): void;
|
|
21
|
+
private observe;
|
|
22
|
+
private onVisibilityChange;
|
|
23
|
+
private captureMetric;
|
|
24
|
+
private flush;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { type WebVitalsOptions, WebVitalsTracker as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunks/web-vitals-BooBPWJC.js";export{e as default};
|
package/package.json
CHANGED
|
@@ -2,34 +2,54 @@
|
|
|
2
2
|
"name": "@faststats/web",
|
|
3
3
|
"repository": {
|
|
4
4
|
"type": "git",
|
|
5
|
-
"url": "https://github.com/faststats-dev/
|
|
5
|
+
"url": "https://github.com/faststats-dev/faststats-javascript.git"
|
|
6
6
|
},
|
|
7
|
-
"main": "dist/
|
|
8
|
-
"module": "dist/
|
|
9
|
-
"types": "dist/
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
-
"types": "./dist/
|
|
13
|
-
"import": "./dist/
|
|
14
|
-
"default": "./dist/
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./feature-flags": {
|
|
17
|
+
"types": "./dist/feature-flags.d.ts",
|
|
18
|
+
"import": "./dist/feature-flags.js",
|
|
19
|
+
"default": "./dist/feature-flags.js"
|
|
20
|
+
},
|
|
21
|
+
"./replay": {
|
|
22
|
+
"types": "./dist/replay.d.ts",
|
|
23
|
+
"import": "./dist/replay.js",
|
|
24
|
+
"default": "./dist/replay.js"
|
|
25
|
+
},
|
|
26
|
+
"./error": {
|
|
27
|
+
"types": "./dist/error.d.ts",
|
|
28
|
+
"import": "./dist/error.js",
|
|
29
|
+
"default": "./dist/error.js"
|
|
30
|
+
},
|
|
31
|
+
"./web-vitals": {
|
|
32
|
+
"types": "./dist/web-vitals.d.ts",
|
|
33
|
+
"import": "./dist/web-vitals.js",
|
|
34
|
+
"default": "./dist/web-vitals.js"
|
|
15
35
|
}
|
|
16
36
|
},
|
|
17
37
|
"type": "module",
|
|
18
|
-
"sideEffects":
|
|
38
|
+
"sideEffects": false,
|
|
19
39
|
"publishConfig": {
|
|
20
40
|
"access": "public"
|
|
21
41
|
},
|
|
22
|
-
"version": "0.2.
|
|
42
|
+
"version": "0.2.8",
|
|
23
43
|
"scripts": {
|
|
24
|
-
"build": "tsdown",
|
|
44
|
+
"build": "tsdown && bun run check-size",
|
|
25
45
|
"dev": "bun run build",
|
|
26
46
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
27
47
|
"test": "bun test",
|
|
48
|
+
"check-size": "node scripts/check-bundle-size.mjs",
|
|
28
49
|
"deploy:worker": "wrangler deploy"
|
|
29
50
|
},
|
|
30
51
|
"devDependencies": {
|
|
31
52
|
"@biomejs/biome": "2.4.2",
|
|
32
|
-
"@rrweb/types": "^2.0.0-alpha.20",
|
|
33
53
|
"@types/bun": "latest",
|
|
34
54
|
"tsdown": "^0.21.4",
|
|
35
55
|
"typescript": "^5.9.3",
|
|
@@ -38,6 +58,7 @@
|
|
|
38
58
|
"dependencies": {
|
|
39
59
|
"@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.20",
|
|
40
60
|
"@rrweb/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.20",
|
|
61
|
+
"@rrweb/types": "^2.0.0-alpha.20",
|
|
41
62
|
"rrweb": "^2.0.0-alpha.4",
|
|
42
63
|
"rrweb-snapshot": "^2.0.0-alpha.4",
|
|
43
64
|
"web-vitals": "^5.1.0"
|
|
@@ -1,15 +1,96 @@
|
|
|
1
|
-
import { stat } from "node:fs/promises";
|
|
2
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { gzipSync } from "node:zlib";
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
const bundlePath = resolve(process.cwd(), "dist/module.js");
|
|
6
|
-
const file = await stat(bundlePath);
|
|
5
|
+
const distDir = resolve(process.cwd(), "dist");
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const ENTRY_BUDGETS = {
|
|
8
|
+
"index.js": { raw: 18 * 1024, gzip: 7 * 1024 },
|
|
9
|
+
"feature-flags.js": { raw: 4 * 1024, gzip: 2 * 1024 },
|
|
10
|
+
"replay.js": { raw: 8 * 1024, gzip: 4 * 1024 },
|
|
11
|
+
"error.js": { raw: 5 * 1024, gzip: 3 * 1024 },
|
|
12
|
+
"web-vitals.js": { raw: 4 * 1024, gzip: 2 * 1024 },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const TOTAL_BUDGET = { raw: 40 * 1024, gzip: 16 * 1024 };
|
|
16
|
+
|
|
17
|
+
function formatBytes(bytes) {
|
|
18
|
+
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function listJs(dir) {
|
|
22
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
23
|
+
const files = [];
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const full = join(dir, entry.name);
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
files.push(...(await listJs(full)));
|
|
28
|
+
} else if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
29
|
+
files.push(full);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const failures = [];
|
|
36
|
+
let totalRaw = 0;
|
|
37
|
+
let totalGzip = 0;
|
|
38
|
+
|
|
39
|
+
for (const [name, budget] of Object.entries(ENTRY_BUDGETS)) {
|
|
40
|
+
const path = join(distDir, name);
|
|
41
|
+
let info;
|
|
42
|
+
try {
|
|
43
|
+
info = await stat(path);
|
|
44
|
+
} catch {
|
|
45
|
+
failures.push(`Missing entry: ${name}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const buffer = await readFile(path);
|
|
49
|
+
const gzipped = gzipSync(buffer).byteLength;
|
|
50
|
+
|
|
51
|
+
const status = [];
|
|
52
|
+
if (info.size > budget.raw) {
|
|
53
|
+
status.push(`raw ${formatBytes(info.size)} > ${formatBytes(budget.raw)}`);
|
|
54
|
+
}
|
|
55
|
+
if (gzipped > budget.gzip) {
|
|
56
|
+
status.push(`gzip ${formatBytes(gzipped)} > ${formatBytes(budget.gzip)}`);
|
|
57
|
+
}
|
|
58
|
+
if (status.length > 0) {
|
|
59
|
+
failures.push(`${name}: ${status.join(", ")}`);
|
|
60
|
+
}
|
|
61
|
+
console.log(
|
|
62
|
+
`${name.padEnd(20)} raw ${formatBytes(info.size).padStart(10)} gzip ${formatBytes(gzipped).padStart(10)}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const allFiles = await listJs(distDir);
|
|
67
|
+
for (const file of allFiles) {
|
|
68
|
+
const buf = await readFile(file);
|
|
69
|
+
totalRaw += buf.byteLength;
|
|
70
|
+
totalGzip += gzipSync(buf).byteLength;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(
|
|
74
|
+
`${"total".padEnd(20)} raw ${formatBytes(totalRaw).padStart(10)} gzip ${formatBytes(totalGzip).padStart(10)}`,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (totalRaw > TOTAL_BUDGET.raw) {
|
|
78
|
+
failures.push(
|
|
79
|
+
`total raw ${formatBytes(totalRaw)} > ${formatBytes(TOTAL_BUDGET.raw)}`,
|
|
11
80
|
);
|
|
81
|
+
}
|
|
82
|
+
if (totalGzip > TOTAL_BUDGET.gzip) {
|
|
83
|
+
failures.push(
|
|
84
|
+
`total gzip ${formatBytes(totalGzip)} > ${formatBytes(TOTAL_BUDGET.gzip)}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (failures.length > 0) {
|
|
89
|
+
console.error("\nBundle size check failed:");
|
|
90
|
+
for (const failure of failures) {
|
|
91
|
+
console.error(` - ${failure}`);
|
|
92
|
+
}
|
|
12
93
|
process.exit(1);
|
|
13
94
|
}
|
|
14
95
|
|
|
15
|
-
console.log(
|
|
96
|
+
console.log("\nBundle size check passed.");
|