@faststats/web 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,94 @@
1
- import{a as e,r as t,t as n}from"./chunks/api-urls-Bh77rElT.js";import{r,t as i}from"./chunks/send-data-CIbuTxPh.js";var a=class{options;endpoint;metrics=new Map;started=!1;flushing=!1;initialUrl=``;currentUrl=``;constructor(r){this.options=r,this.endpoint=`${e(r.baseUrl,n)}${t.vitals}`}async start(){if(this.started||typeof window>`u`)return;this.started=!0,this.initialUrl=window.location.href,this.currentUrl=this.initialUrl;let e=await(this.options.attribution?import(`web-vitals/attribution`):import(`web-vitals`));if(!this.started)return;let t={reportAllChanges:!0};e.onCLS(this.onMetric,t),e.onINP(this.onMetric,t),e.onLCP(this.onMetric,t),e.onFCP(this.onMetric),e.onTTFB(this.onMetric)}stop(){this.started&&(this.started=!1,this.flush())}onPageHidden(e=!1){e||this.flush()}trackPageChange(e){if(!this.started)return;let t=e??window.location.href;t!==this.currentUrl&&(this.currentUrl=t)}onMetric=e=>{if(!this.started)return;let t=e.name,n=this.getMetricUrl(t),{id:r,rating:i,delta:a,navigationType:o}=e,s=`attribution`in e&&e.attribution?e.attribution:void 0;this.metrics.set(`${n}\n${t}`,{metric:t,url:n,value:e.value,attributes:{id:r,rating:i,delta:a,navigationType:o,...s}})};getMetricUrl(e){return e===`FCP`||e===`LCP`||e===`TTFB`?this.initialUrl:this.currentUrl||this.initialUrl}flush(){if(!this.metrics.size||this.flushing)return;this.flushing=!0;let e=this.metrics;this.metrics=new Map;let t=r(this.options.siteKey),n=new Map;for(let t of e.values()){let e=n.get(t.url)??[];e.push(t),n.set(t.url,e)}Promise.all([...n].map(([e,n])=>i({url:this.endpoint,data:JSON.stringify({token:this.options.siteKey,sessionId:t.sessionId,windowId:t.windowId,vitals:n.map(({url:e,...t})=>t),metadata:{url:e}}),debug:this.options.debug,debugPrefix:`[WebVitals]`}).then(t=>({ok:t,url:e,vitals:n})))).then(e=>{for(let t of e)if(!t.ok)for(let e of t.vitals){let n=`${t.url}\n${e.metric}`;this.metrics.has(n)||this.metrics.set(n,e)}this.flushing=!1})}};export{a as default};
1
+ import { a as resolveBaseUrl, o as sanitizeUrl, r as URLS, t as ANALYTICS_BASE } from "./chunks/api-urls-Dp2XNjRF.js";
2
+ import { r as getSessionContext, t as sendData } from "./chunks/send-data-DR1JUXka.js";
3
+ //#region src/web-vitals.ts
4
+ const METRIC_NAMES = /* @__PURE__ */ new Set([
5
+ "CLS",
6
+ "INP",
7
+ "LCP",
8
+ "FCP",
9
+ "TTFB"
10
+ ]);
11
+ var WebVitalsTracker = class {
12
+ options;
13
+ endpoint;
14
+ metrics = /* @__PURE__ */ new Map();
15
+ started = false;
16
+ flushing = false;
17
+ initialUrl = "";
18
+ session = null;
19
+ constructor(options) {
20
+ this.options = options;
21
+ this.endpoint = `${resolveBaseUrl(options.baseUrl, ANALYTICS_BASE)}${URLS.vitals}`;
22
+ }
23
+ async start() {
24
+ if (this.started || typeof window === "undefined") return;
25
+ this.started = true;
26
+ this.initialUrl = sanitizeUrl(window.location.href, this.options.trackHash ?? false);
27
+ this.session = getSessionContext(this.options.siteKey);
28
+ const mod = await (this.options.attribution ? import("web-vitals/attribution") : import("web-vitals"));
29
+ if (!this.started) return;
30
+ const changes = { reportAllChanges: true };
31
+ mod.onCLS(this.onMetric, changes);
32
+ mod.onINP(this.onMetric, changes);
33
+ mod.onLCP(this.onMetric, changes);
34
+ mod.onFCP(this.onMetric);
35
+ mod.onTTFB(this.onMetric);
36
+ }
37
+ stop(discard = false) {
38
+ if (!this.started) return;
39
+ this.started = false;
40
+ if (discard) this.metrics.clear();
41
+ else this.flush();
42
+ }
43
+ onPageHidden(persisted = false) {
44
+ if (persisted) return;
45
+ queueMicrotask(() => void this.flush());
46
+ }
47
+ onMetric = (metric) => {
48
+ if (!this.started) return;
49
+ const name = metric.name;
50
+ if (!METRIC_NAMES.has(name) || !Number.isFinite(metric.value) || metric.value < 0) return;
51
+ const { id, rating, delta, navigationType } = metric;
52
+ const attribution = "attribution" in metric && metric.attribution ? metric.attribution : void 0;
53
+ this.metrics.set(name, {
54
+ metric: name,
55
+ value: metric.value,
56
+ attributes: {
57
+ id,
58
+ rating,
59
+ delta,
60
+ navigationType,
61
+ ...attribution
62
+ }
63
+ });
64
+ };
65
+ async flush() {
66
+ if (!this.metrics.size || this.flushing) return;
67
+ this.flushing = true;
68
+ const batch = this.metrics;
69
+ this.metrics = /* @__PURE__ */ new Map();
70
+ const session = this.session ?? getSessionContext(this.options.siteKey);
71
+ let sent = false;
72
+ try {
73
+ sent = await sendData({
74
+ url: this.endpoint,
75
+ data: JSON.stringify({
76
+ token: this.options.siteKey,
77
+ sessionId: session.sessionId,
78
+ windowId: session.windowId,
79
+ vitals: [...batch.values()],
80
+ metadata: { url: this.initialUrl }
81
+ }),
82
+ debug: this.options.debug,
83
+ debugPrefix: "[WebVitals]"
84
+ });
85
+ } finally {
86
+ if (!sent) {
87
+ for (const [name, vital] of batch) if (!this.metrics.has(name)) this.metrics.set(name, vital);
88
+ }
89
+ this.flushing = false;
90
+ }
91
+ }
92
+ };
93
+ //#endregion
94
+ export { WebVitalsTracker as default };
package/package.json CHANGED
@@ -42,7 +42,7 @@
42
42
  "publishConfig": {
43
43
  "access": "public"
44
44
  },
45
- "version": "0.5.0",
45
+ "version": "0.7.0",
46
46
  "scripts": {
47
47
  "build": "tsdown && bun run check-size",
48
48
  "dev": "bun run build",
@@ -60,6 +60,6 @@
60
60
  "@rrweb/record": "^2.1.0",
61
61
  "@rrweb/rrweb-plugin-console-record": "^2.1.0",
62
62
  "@rrweb/rrweb-plugin-sequential-id-record": "^2.1.0",
63
- "web-vitals": "^5.3.0"
63
+ "web-vitals": "^6.0.0"
64
64
  }
65
65
  }
@@ -1 +0,0 @@
1
- const e=`https://metrics.faststats.dev`,t=`https://flags.faststats.dev`;function n(e,t){return e?.replace(/\/+$/,``)||t}const r={events:`/v1/web`,identify:`/v1/identify`,replay:`/v1/replay`,vitals:`/v1/vitals`,flags:`/v1/check`},i=[`utm_source`,`utm_medium`,`utm_campaign`,`utm_term`,`utm_content`];function a(){let e={page:location.pathname,url:location.href,referrer:document.referrer||null,title:document.title||``};if(!location.search)return e;let t=new URLSearchParams(location.search);for(let n of i){let r=t.get(n);r&&(e[n]=r)}return e}export{n as a,a as i,t as n,r,e as t};
@@ -1 +0,0 @@
1
- import{i as e,n as t}from"./send-data-CIbuTxPh.js";const n=`faststats_anon_id`;function r(){try{return globalThis.localStorage}catch{return}}function i(){let e=r();if(!e)return``;try{let r=e.getItem(n);if(r)return r;let i=t();return e.setItem(n,i),i}catch{return``}}function a(t){return t??e()?``:i()}function o(e){if(e)return``;try{let e=r();if(!e)return``;e.removeItem(n)}catch{return``}return i()}export{o as n,a as t};
@@ -1 +0,0 @@
1
- const e=`faststats_session`;let t=!1,n=null;const r=new Set;function i(e){try{return globalThis[e]}catch{return null}}function a(){return typeof crypto<`u`&&`randomUUID`in crypto?crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function o(){let t=i(`localStorage`);if(!t)return n;let r=t.getItem(e);if(r)try{let e=JSON.parse(r);if(e.id)return e}catch{}return n}function s(t){let r=i(`localStorage`);if(!r){n=t;return}try{r.setItem(e,JSON.stringify(t))}catch{n=t}}function c(){n=null;try{i(`localStorage`)?.removeItem(e)}catch{}}function l(e,t){return t-e.activity>=18e5||t-e.start>=864e5}function u(e,t){for(let n of r)n(e,t)}function d(e,t){let r=Date.now();if(e)return n?t&&(n.activity=r):n={id:a(),start:r,activity:r},{session:n,rotated:!1,previous:null};let i=o();if(i&&!l(i,r))return t&&(i.activity=r,s(i)),{session:i,rotated:!1,previous:null};let c=i,u={id:a(),start:r,activity:r};return s(u),{session:u,rotated:c!==null,previous:c}}function f(e,t,n){if(t)return n;let r=`faststats_window_id_${e}`,o=i(`sessionStorage`);if(!o)return n;try{let e=o.getItem(r);if(e)return e;let t=a();return o.setItem(r,t),t}catch{return n}}function p(e,t,n,r){let i=r??f(e,n,t.id);return{sessionId:t.id,windowId:i,sessionStart:t.start}}function m(e){t=e,n=null}function h(){return t}function g(e,n){let r=n??t,{session:i}=d(r,!1);return p(e,i,r)}function _(e,n){let r=n??t,{session:i,rotated:a,previous:o}=d(r,!0);if(!a||!o)return null;let s=f(e,r,i.id),c={prev:p(e,o,r,s),next:p(e,i,r,s)};return u(c.prev,c.next),c}function v(e){let r=t,o=g(e,r);if(c(),r){let e=Date.now();n={id:a(),start:e,activity:e}}else try{i(`sessionStorage`)?.removeItem(`faststats_window_id_${e}`)}catch{}let s=g(e,r);return o.sessionId!==s.sessionId&&u(o,s),s}function y(e){return r.add(e),()=>r.delete(e)}async function b(e){let{url:t,data:n,contentType:r=`application/json`,headers:i={},debug:a=!1,debugPrefix:o=`[Analytics]`,useBeacon:s=!0,keepalive:c=!0,signal:l}=e;if(l?.aborted)return!1;if(s&&typeof globalThis.navigator?.sendBeacon==`function`)try{let e=n instanceof Blob||typeof Blob>`u`?n:new Blob([n],{type:r});if(globalThis.navigator.sendBeacon(t,e))return a&&console.log(`${o} Sent via beacon`),!0}catch{}try{let e=await fetch(t,{method:`POST`,body:n,headers:{"Content-Type":r,...i},keepalive:c,credentials:`omit`,signal:l}),s=e.ok;if(a){let t=s?`${o} Sent via fetch`:`${o} Failed: ${e.status}`;console[s?`log`:`warn`](t)}return s}catch{return a&&console.warn(`${o} Failed to send`),!1}}export{y as a,_ as c,h as i,a as n,v as o,g as r,m as s,b as t};