@iann29/rastro 0.1.0-alpha.8 → 0.1.0-alpha.9
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/README.md +28 -18
- package/agent/manifest.json +2 -2
- package/dist/client/index.d.ts +7 -7
- package/dist/component/_generated/component.d.ts +3 -3
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/constants.d.ts +2 -1
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +2 -1
- package/dist/component/constants.js.map +1 -1
- package/dist/component/eventStore.d.ts +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +1 -0
- package/dist/component/http.js.map +1 -1
- package/dist/component/ingest.d.ts +1 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +30 -2
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/reports.d.ts +2 -2
- package/dist/component/schema.d.ts +4 -4
- package/dist/component/validators.d.ts +7 -7
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +1 -1
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +4 -4
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +4 -4
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -1
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +24 -2
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker.min.js +1 -1
- package/docs/upgrading.md +38 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/component/_generated/component.ts +3 -0
- package/src/component/constants.ts +2 -1
- package/src/component/http.ts +1 -0
- package/src/component/ingest.ts +51 -1
- package/src/component/validators.ts +1 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by scripts/build-tracker.mjs. Do not edit.
|
|
2
|
-
export const TRACKER_SOURCE = "(()=>{const e=document.currentScript,t=e?.dataset.site,s=e?.dataset.endpoint||e?.src&&new URL(\"events\",e.src).href;if(!t||!s)return;const o=()=>crypto.randomUUID?.()||Math.random()+\"\",n=new URLSearchParams(location.search),r=n.get(\"ref\")?.slice(0,64);let a,i,c;for(const e of[\"source\",\"medium\",\"campaign\",\"term\",\"content\"]){const t=n.get(\"utm_\"+e)?.slice(0,64);t&&((a??={})[\"utm_\"+e]=t)}try{i=sessionStorage._r||o(),sessionStorage._r=i,c=r||sessionStorage._a,c&&(sessionStorage._a=c),a||=sessionStorage._u&&JSON.parse(sessionStorage._u),a&&(sessionStorage._u=JSON.stringify(a))}catch{i=o(),c=r}
|
|
3
|
-
export const TRACKER_GZIP_BASE64 = "
|
|
4
|
-
export const TRACKER_RAW_BYTES =
|
|
5
|
-
export const TRACKER_GZIP_BYTES =
|
|
2
|
+
export const TRACKER_SOURCE = "(()=>{const e=document.currentScript,t=e?.dataset.site,s=e?.dataset.endpoint||e?.src&&new URL(\"events\",e.src).href;if(!t||!s)return;const o=()=>crypto.randomUUID?.()||Math.random()+\"\",n=new URLSearchParams(location.search),r=n.get(\"ref\")?.slice(0,64);let a,i,c;for(const e of[\"source\",\"medium\",\"campaign\",\"term\",\"content\"]){const t=n.get(\"utm_\"+e)?.slice(0,64);t&&((a??={})[\"utm_\"+e]=t)}try{i=sessionStorage._r||o(),sessionStorage._r=i,c=r||sessionStorage._a,c&&(sessionStorage._a=c),a||=sessionStorage._u&&JSON.parse(sessionStorage._u),a&&(sessionStorage._u=JSON.stringify(a))}catch{i=o(),c=r}let d=e.dataset.visitor;try{d||=localStorage._rv||=o()}catch{}d||=i;let l,p=0;const g=[],h=document.referrer?new URL(document.referrer).origin:void 0,u=e=>{const o=g.splice(0,50);if(!o.length)return;const n=()=>new Blob([JSON.stringify({siteId:t,events:o,sentAt:Date.now()})]);let r=n();for(;r.size>64e3&&o.length>1;)g.unshift(o.pop()),r=n();e&&navigator.sendBeacon(s,r)||fetch(s,{method:\"POST\",body:r,keepalive:1}).catch(()=>{}),g.length&&u(e)},m=(e,t,s)=>{const o=Date.now();g.push({eventId:`${i}.${o.toString(36)}.${p}`,sessionId:i,visitorId:d,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:o,sequence:p++,affiliateSlug:c,...s}),clearTimeout(l),l=setTimeout(u,1200)};let v;const f=()=>{const e=location.pathname;e!==v&&(v=e,m(\"pageview\",void 0,a&&{properties:a}))};window.rastro=(e,t,s)=>{if(\"context\"===e)return{sessionId:i,visitorId:d};\"event\"===e&&m(\"custom\",`${t||\"event\"}`.slice(0,80),s&&JSON.stringify(s).length>2e4?void 0:{properties:s})},document.addEventListener(\"click\",e=>{const t=e.target?.closest(\"a,button,[data-rastro-event]\");if(!t)return;const s=t.dataset.rastroEvent,o=t.href,n=o&&new URL(o),r=/^https?:$/.test(n?.protocol),a=r&&n.origin!==location.origin;m(s?\"custom\":a?\"outbound\":\"click\",s,{target:(t.dataset.rastroLabel||t.innerText||t.tagName).slice(0,64),href:r?((a?n.origin:\"\")+n.pathname).slice(0,256):void 0})},1);const S=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),f()}};S(\"pushState\"),S(\"replaceState\"),addEventListener(\"popstate\",f),addEventListener(\"pagehide\",()=>{m(\"leave\"),u(1)}),addEventListener(\"pageshow\",e=>{e.persisted&&(m(\"heartbeat\"),u(1))}),setInterval(()=>{m(\"heartbeat\"),u()},2e4),f()})();";
|
|
3
|
+
export const TRACKER_GZIP_BASE64 = "H4sIAAAAAAACA3VV227cNhD9FZswBBKeMLbjGIUEZtEgfXDhJkE3eTLcmKZGEhGJVMnROtuV/r2QVuv1JXmTZsi5nDlnyLlQ7zbGu0gHqHJvugYdSdOFgI6WJtiWgBQuZK5JRyQZLSHExxZ0eeuto77HhYzBJInD+4Ovf19xhit0FBngaBeyClhktuCH1PeHUQSkLrhsm92rsRQT1i15GbTLffP16+WHheSi7//SVM1GLo4ZA6fmHEvUwVSfddBN5LU3mqx3Mk5WAUE5WSJxFrBgYiFjbQ3yE7g4F1mNdKDBgskKH/gMwYEvrln0XTDIgDWY265hwIxuWm1Lx4ARhsniHaEjdiNm9GiXqqPmGzvGZ9koSTjXi4XaDOJ6d+ZGkRgorDdWRYzRerckH3SJ8lvoe88FvDArC0aFvn/u0GCShL+wKiNA9/2L8F2S/Ln89FG2OkR8ca0ToH8SrVPTnUjButIWa66FGIwmU22sGqs1KgwjrLnCB3asbLTkQza2mfe9GmdU7/tZ9f14dQ4zjCfsNJoaWnUyc6NU1zdQ7ekZsMAQMCx2RHvhEdIHW1qXrrzND06gU/jAc69KGdt5Nm9PxMRIL2t0JVVPSekmUo5Z3tf+jl8/638ziuEyTwm2RE89RHT0O6UfNKF0/p6LQdxsuRaU42LiWhZktP/hu4tzfJMku8zvTjNRys7FyhbEvWx9y8VEYS4yTBKnV7bU5IOM6PL3qI13PEIQfV8gmYpH2DRIlc9T9vnT8guDO5+v0wDfEVtd2xWmp4OQE9B8kv0goJyTJ0nHUQzQKI5AEMUjtPa9ZKVsu1jxzdTuZZ7eHm3sII82XpJfTrDwNxditLTD7Y68l3lqYabBZZ7mQOsWUwSnG0wJWk1V+qDc8W90PIjn7O2FgN1Y0wrINhhJN+0E9r8dOoNpe3wMuihsbTXhsu7K1ICUMg4CTI06fLEN+o54LaBWEWn338Hp2cmJGKYBreahF+rJUnxRWoaHSq2ShK8UQsNZq0tcWbxnMJNNJ8mmDb7FQBZjqgchhuzeutzfy6AjBf8IZVvw7TL5QUwphTMBN78Ab8i2S3U6myQNZ6aL5BsGt0cb6vvZO9w+APjbiYA4y31P3Sh2tDvD88W28PRx1XEQAzzoSuf5H2PgKxsJHQbOTG3NdwZ7WZFCSTqUSAtpah8xEmca7joi7+B63Aevtt2/mmq8YVvl0VPJRUUPu2N7fEoMXtH0foBTfv/C+FEgr/+piNq4SI9eSxrTuoVsgydvfC1Aq5Akbt4Hh2o/z60la3hc7EBM9YL5ju5853KW7nqMsNk2lvLntV3pO6z7nqR1DsMX/EHjD+nyo25QPH4BYCw+DYvxFdjlThkTx3tiiSekn1fXOIZTMYOzVI/xrmwkH9bXeJPtPxWXUuLILJK6bes1n32AAgouhiFbcjbKeEmakAlYjs9jW2uDO8vLabe+jZMTip+6dYmVzZHBJJ2Gsxr1agzV8VMx/OpKrPz9lkIoWwxxdOZJwhvOKtSB7lDTHGMMEpEuHWFY6Zrv8jw9JwY4w/Ntn4KL7H/zTKaM3wgAAA==";
|
|
4
|
+
export const TRACKER_RAW_BYTES = 2271;
|
|
5
|
+
export const TRACKER_GZIP_BYTES = 1177;
|
|
6
6
|
export const VITALS_SOURCE = "(()=>{const t=document.currentScript,e=t?.dataset.site,n=t?.dataset.endpoint||t?.src&&new URL(\"events\",t.src).href;if(!e||!n)return;const r=()=>crypto.randomUUID?.()||Math.random()+\"\";let a;try{a=sessionStorage._r||r(),sessionStorage._r=a}catch{a=r()}const s=t.dataset.visitor||a,o=location.pathname.slice(0,256),i={},c=[],d=(t,e,n)=>{try{const r=new PerformanceObserver(t=>e(t.getEntries()));r.observe({type:t,buffered:1,...n}),c.push({observer:r,handler:e})}catch{}},h=performance.getEntriesByType?.(\"navigation\")[0];h&&h.responseStart>0&&(i.TTFB=h.responseStart),d(\"paint\",t=>{for(const e of t)\"first-contentful-paint\"===e.name&&(i.FCP=e.startTime)}),d(\"largest-contentful-paint\",t=>{const e=t[t.length-1];e&&(i.LCP=e.startTime)});let l=0,f=0,m=0;d(\"layout-shift\",t=>{for(const e of t)e.hadRecentInput||(l&&e.startTime-m<1e3&&e.startTime-f<5e3?l+=e.value:(l=e.value,f=e.startTime),m=e.startTime,i.CLS=Math.max(i.CLS||0,1e3*l))});const u=[],p=new Set,v=t=>{for(const e of t)e.interactionId&&(p.add(e.interactionId),u.push(e.duration));if(!u.length)return;u.sort((t,e)=>e-t),u.length=Math.min(u.length,10);const e=performance.interactionCount||p.size;i.INP=u[Math.min(u.length-1,Math.floor(e/50))]};d(\"event\",v,{durationThreshold:40}),d(\"first-input\",v);let g=0;const y=()=>{if(g)return;for(const{observer:t,handler:e}of c)e(t.takeRecords()),t.disconnect();const t=Date.now(),r=Object.keys(i).map((e,n)=>({eventId:`${a}.${t.toString(36)}.v${n}`,sessionId:a,visitorId:s,type:\"vital\",name:e,value:Math.round(i[e]),path:o,timestamp:t,sequence:n}));if(!r.length)return;g=1;const d=new Blob([JSON.stringify({siteId:e,events:r,sentAt:t})]),h=navigator.sendBeacon?.bind(navigator);h&&h(n,d)||fetch(n,{method:\"POST\",body:d,keepalive:1}).catch(()=>{})};document.addEventListener(\"visibilitychange\",()=>{\"hidden\"===document.visibilityState&&y()}),addEventListener(\"pagehide\",y)})();";
|
|
7
7
|
export const VITALS_GZIP_BASE64 = "H4sIAAAAAAACA3VV72/bNhD9V1IhMMiV5uxl6Qe5TLCkLeAha4I4+RQEK02eLC4SqZEnd5qk/32gZOfn+k06kXfv3b17IoSKk1Y5G/AAhXaqLsEiV7X3YHGlvKmQgcBTriXKAMiDQWD2eQSsrpyx2HV4yoNXk4mF7we31xckgS1YDAnDGKc895AtTEbeQde9s9QD1t4uxupeRCjKNxU67qXVrry9XX465YR23R8S812Q0PdJsigAD+QCfdNKESAE4+wKnZcb4H/6rvOEsjdhIXslUeWtFJ7QfiwbBD4S2Zpg0Pmuk8yJwimJxlleScytLIGHwiggM/bL8QfKjGh7psTdPdOCIANmYyMjoD2d2IQr8JnzpbQKLtcB/BY8QXECBPkG8LNFbyAQSunCczceIC02FaTI1nWWgQedzhnn3PaUKV7VISft7qRPPcul1QX4FHq6I9f3LBfVU91nhc6am6aCU04SK7dmM9BL6N3sfpFPJjn3ECpnA6xQejyZTSbE8JubL2fi1SfKNEkqaSwmDMVJmzlPRtJw4LIDpElmfMCpchbBYlYX0/G0EAJ4bOWQ+sv5lQAeYsYbUwLth7yF9Bv4v8tDqV0ZgXfIC7AbzKfz+8WY7+JNvkElhZixTMxYKWaLIX/japyG3GQ/gg88l/oaFFhc2qrGriPFZPIs9bT8OIejl6Hs4zEcnRbvBfCtLGpISbF/ZNkLXKx8/soMP79YiUHgpfyHDK9dN2NzOPqpoJHFCK6OWqsGVa0A2Vb8ALuxCF6qONulnkxIxaXW5FWcsnrUEnBd+0EIlA6bWe/6ul/OmgfnkUSJU3ECU4xXxyM70MaSfYTNZ3u48EKDz4qfuzpaRcWD+RcWhi+/Xon67k2q6ZwNsaxwzhP4+XhG6X0fJzh4SsK2rN1Dv8k9hNwVOv11NqpoFKCJ00vYdhTCRsx22JrBalqTkc2e5mMjn5YLny2Xyw4UjVuL8gGuQTmv49oy5NoE5awFhWRPHcUnicCt+04o8+Jy/Rco5A/QBGIoL2VFyGgXpB24LHX67bCVPT9skaNboTd2Q44+0J5vD1vbf9tb2VKnku1MaqnTwAanSLYGZZGwuFgpsFF9o2G62mpi7uCesuhiqWNoSggoyypFFuDvGqyC1Pa74ftXw9+I+Y6THoR3Vrg1uft9dfmVhwGlyRrSxj/CUqfARrdPPQtg8TdMsaf3lOVi5zbO8wBWn4FUzp7ytbGaPH6igwcRyzTtugxQxee2BMydTpOry9VNwtZON6lmDwCVLMwW0nlP+eB6ZBhoT/vF4y9Mav054rkwAcGCJ0ns3NoUBhuVS7uBhA23ktxoDTa60+Plp6MrlAiTSUOiP73NWckN5EZDwhraU0IX/wGQzzziUQcAAA==";
|
|
8
8
|
export const VITALS_RAW_BYTES = 1873;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated.js","sourceRoot":"","sources":["../../src/tracker/generated.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,CAAC,MAAM,cAAc,GAAG,
|
|
1
|
+
{"version":3,"file":"generated.js","sourceRoot":"","sources":["../../src/tracker/generated.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,CAAC,MAAM,cAAc,GAAG,6xEAA6xE,CAAC;AAC5zE,MAAM,CAAC,MAAM,mBAAmB,GAAG,siDAAsiD,CAAC;AAC1kD,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,MAAM,CAAC,MAAM,aAAa,GAAG,+2DAA+2D,CAAC;AAC74D,MAAM,CAAC,MAAM,kBAAkB,GAAG,k3CAAk3C,CAAC;AACr5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type RastroProperty = string | number | boolean | null;
|
|
2
2
|
type RastroProperties = Record<string, RastroProperty>;
|
|
3
|
-
type RastroEventType = "pageview" | "click" | "custom" | "conversion" | "heartbeat" | "outbound";
|
|
3
|
+
type RastroEventType = "pageview" | "click" | "custom" | "conversion" | "heartbeat" | "outbound" | "leave";
|
|
4
4
|
interface RastroEvent {
|
|
5
5
|
eventId: string;
|
|
6
6
|
sessionId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/tracker/tracker.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AACvD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACvD,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/tracker/tracker.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AACvD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACvD,KAAK,eAAe,GAChB,UAAU,GACV,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,UAAU,GACV,OAAO,CAAC;AAEZ,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,aAAa,GAAG,CACnB,OAAO,EAAE,OAAO,GAAG,SAAS,EAC5B,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,gBAAgB,KAC1B,aAAa,GAAG,IAAI,CAAC;AAE1B,UAAU,YAAa,SAAQ,MAAM;IACnC,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB"}
|
package/dist/tracker/tracker.js
CHANGED
|
@@ -32,7 +32,18 @@
|
|
|
32
32
|
sessionId = createId();
|
|
33
33
|
affiliate = referral;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
// The identity, in order: the host's signed-in id, the persistent
|
|
36
|
+
// pseudonymous id (one random id per browser and site origin, kept in
|
|
37
|
+
// localStorage), and, when storage is blocked, the per-tab session id —
|
|
38
|
+
// which the server then counts through its salted daily hash.
|
|
39
|
+
let visitorId = script.dataset.visitor;
|
|
40
|
+
try {
|
|
41
|
+
visitorId ||= localStorage._rv ||= createId();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Blocked storage never breaks the page; the session still counts.
|
|
45
|
+
}
|
|
46
|
+
visitorId ||= sessionId;
|
|
36
47
|
let sequence = 0;
|
|
37
48
|
let flushTimer;
|
|
38
49
|
const queue = [];
|
|
@@ -124,7 +135,18 @@
|
|
|
124
135
|
wrapHistory("pushState");
|
|
125
136
|
wrapHistory("replaceState");
|
|
126
137
|
addEventListener("popstate", pageview);
|
|
127
|
-
addEventListener("pagehide", () =>
|
|
138
|
+
addEventListener("pagehide", () => {
|
|
139
|
+
emit("leave");
|
|
140
|
+
flush(true);
|
|
141
|
+
});
|
|
142
|
+
addEventListener("pageshow", (event) => {
|
|
143
|
+
// Back from the bfcache: the leave above already shortened this session's
|
|
144
|
+
// live presence, so revive it immediately instead of waiting a beat.
|
|
145
|
+
if (event.persisted) {
|
|
146
|
+
emit("heartbeat");
|
|
147
|
+
flush(true);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
128
150
|
setInterval(() => {
|
|
129
151
|
emit("heartbeat");
|
|
130
152
|
flush();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/tracker/tracker.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/tracker/tracker.ts"],"names":[],"mappings":"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"}
|
package/dist/tracker.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const e=document.currentScript,t=e?.dataset.site,s=e?.dataset.endpoint||e?.src&&new URL("events",e.src).href;if(!t||!s)return;const o=()=>crypto.randomUUID?.()||Math.random()+"",n=new URLSearchParams(location.search),r=n.get("ref")?.slice(0,64);let a,i,c;for(const e of["source","medium","campaign","term","content"]){const t=n.get("utm_"+e)?.slice(0,64);t&&((a??={})["utm_"+e]=t)}try{i=sessionStorage._r||o(),sessionStorage._r=i,c=r||sessionStorage._a,c&&(sessionStorage._a=c),a||=sessionStorage._u&&JSON.parse(sessionStorage._u),a&&(sessionStorage._u=JSON.stringify(a))}catch{i=o(),c=r}
|
|
1
|
+
(()=>{const e=document.currentScript,t=e?.dataset.site,s=e?.dataset.endpoint||e?.src&&new URL("events",e.src).href;if(!t||!s)return;const o=()=>crypto.randomUUID?.()||Math.random()+"",n=new URLSearchParams(location.search),r=n.get("ref")?.slice(0,64);let a,i,c;for(const e of["source","medium","campaign","term","content"]){const t=n.get("utm_"+e)?.slice(0,64);t&&((a??={})["utm_"+e]=t)}try{i=sessionStorage._r||o(),sessionStorage._r=i,c=r||sessionStorage._a,c&&(sessionStorage._a=c),a||=sessionStorage._u&&JSON.parse(sessionStorage._u),a&&(sessionStorage._u=JSON.stringify(a))}catch{i=o(),c=r}let d=e.dataset.visitor;try{d||=localStorage._rv||=o()}catch{}d||=i;let l,p=0;const g=[],h=document.referrer?new URL(document.referrer).origin:void 0,u=e=>{const o=g.splice(0,50);if(!o.length)return;const n=()=>new Blob([JSON.stringify({siteId:t,events:o,sentAt:Date.now()})]);let r=n();for(;r.size>64e3&&o.length>1;)g.unshift(o.pop()),r=n();e&&navigator.sendBeacon(s,r)||fetch(s,{method:"POST",body:r,keepalive:1}).catch(()=>{}),g.length&&u(e)},m=(e,t,s)=>{const o=Date.now();g.push({eventId:`${i}.${o.toString(36)}.${p}`,sessionId:i,visitorId:d,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:o,sequence:p++,affiliateSlug:c,...s}),clearTimeout(l),l=setTimeout(u,1200)};let v;const f=()=>{const e=location.pathname;e!==v&&(v=e,m("pageview",void 0,a&&{properties:a}))};window.rastro=(e,t,s)=>{if("context"===e)return{sessionId:i,visitorId:d};"event"===e&&m("custom",`${t||"event"}`.slice(0,80),s&&JSON.stringify(s).length>2e4?void 0:{properties:s})},document.addEventListener("click",e=>{const t=e.target?.closest("a,button,[data-rastro-event]");if(!t)return;const s=t.dataset.rastroEvent,o=t.href,n=o&&new URL(o),r=/^https?:$/.test(n?.protocol),a=r&&n.origin!==location.origin;m(s?"custom":a?"outbound":"click",s,{target:(t.dataset.rastroLabel||t.innerText||t.tagName).slice(0,64),href:r?((a?n.origin:"")+n.pathname).slice(0,256):void 0})},1);const S=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),f()}};S("pushState"),S("replaceState"),addEventListener("popstate",f),addEventListener("pagehide",()=>{m("leave"),u(1)}),addEventListener("pageshow",e=>{e.persisted&&(m("heartbeat"),u(1))}),setInterval(()=>{m("heartbeat"),u()},2e4),f()})();
|
package/docs/upgrading.md
CHANGED
|
@@ -16,6 +16,7 @@ deployment.
|
|
|
16
16
|
| `0.1.0-alpha.3` through `alpha.5` with canonical data | Install `0.1.0-alpha.6` directly; Web Vitals tables are additive | Schema deploys and `/rastro/health` reports `webVitals: "optIn"` |
|
|
17
17
|
| `0.1.0-alpha.6` | Install `0.1.0-alpha.7` directly; no schema change | `GET /rastro/tracker.js` contains `sentAt` |
|
|
18
18
|
| `0.1.0-alpha.7` | Install `0.1.0-alpha.8` directly; site map tables are additive; re-export `siteMap` | `/rastro/health` reports `siteMap: "dailyRollup"` |
|
|
19
|
+
| `0.1.0-alpha.8` | Install `0.1.0-alpha.9` directly; no schema change; the tracker changes | `/rastro/health` reports `liveDepartures: "leaveBeacon"` |
|
|
19
20
|
| Any release whose packed declarations lack federation | Stop and request an eligible exact registry release | Packed declarations contain the federation exports |
|
|
20
21
|
|
|
21
22
|
Do not upgrade a populated legacy deployment directly to a release that removes
|
|
@@ -170,6 +171,41 @@ Acceptance for this upgrade: `/rastro/health` reports
|
|
|
170
171
|
between two pages, `siteMap` returns both routes and the transition between
|
|
171
172
|
them.
|
|
172
173
|
|
|
174
|
+
## Realtime presence and persistent identity (alpha.9)
|
|
175
|
+
|
|
176
|
+
`0.1.0-alpha.9` changes no schema, but it changes the served tracker twice over,
|
|
177
|
+
so the cache window is the whole upgrade story.
|
|
178
|
+
|
|
179
|
+
Live presence becomes realtime: the tracker announces a `leave` on `pagehide`
|
|
180
|
+
and the live session retires about ten seconds later, a back/forward-cache
|
|
181
|
+
return revives it immediately, and the safety net for a browser that never says
|
|
182
|
+
goodbye is 90 seconds (down from five minutes). A leave stores no telemetry and
|
|
183
|
+
never extends presence, so replays are inert.
|
|
184
|
+
|
|
185
|
+
Anonymous identity becomes persistent: the tracker keeps one pseudonymous id per
|
|
186
|
+
browser and site origin in `localStorage` and sends it as `visitorId`, so one
|
|
187
|
+
person in several tabs counts once, returning visitors count once across days,
|
|
188
|
+
and journeys join across sessions. A host-supplied `data-visitor` id still wins
|
|
189
|
+
and never touches the stored id; blocked storage falls back to the per-tab id
|
|
190
|
+
and the salted daily hash exactly as before.
|
|
191
|
+
|
|
192
|
+
**Review the host's privacy disclosures before deploying.** The persistent
|
|
193
|
+
visitor ID is an identifier under most privacy laws; the README's privacy
|
|
194
|
+
contract describes what is stored (origin-scoped, never a cookie, gone when the
|
|
195
|
+
visitor clears site data) and the consent note to reconcile with local
|
|
196
|
+
requirements.
|
|
197
|
+
|
|
198
|
+
**The tracker is cached** (`max-age=300, stale-while-revalidate=86400`): for up
|
|
199
|
+
to a day after the deploy, returning visitors and already-open tabs run the old
|
|
200
|
+
tracker — per-tab identity, no leave beacon. They degrade cleanly to the
|
|
201
|
+
90-second safety net and to per-tab counting until the cache turns over.
|
|
202
|
+
Validate in a private window, not in a tab that was open during the deploy.
|
|
203
|
+
|
|
204
|
+
Acceptance for this upgrade: `/rastro/health` reports
|
|
205
|
+
`features.liveDepartures: "leaveBeacon"`, `GET /rastro/tracker.js` contains
|
|
206
|
+
`leave` and `_rv`, two tabs on the site appear as one active visitor, and
|
|
207
|
+
closing both removes the visitor from **Ao vivo** within seconds.
|
|
208
|
+
|
|
173
209
|
## Federation releases
|
|
174
210
|
|
|
175
211
|
Federation is additive to component telemetry but requires host tables,
|
|
@@ -201,6 +237,8 @@ control-plane issuer.
|
|
|
201
237
|
`vitalsReport` reflects a real page load.
|
|
202
238
|
- On `alpha.8` or later: the federation module re-exports `siteMap` and, after a
|
|
203
239
|
real two-page visit, `siteMap` returns both routes and their transition.
|
|
240
|
+
- On `alpha.9` or later: closing the site's last tab removes the visitor from
|
|
241
|
+
the live view within seconds, and two tabs count as one visitor.
|
|
204
242
|
- Protected overview, journey, goal, funnel, and affiliate reports used by the
|
|
205
243
|
host still run.
|
|
206
244
|
- Trusted conversion idempotency and currency checks still pass.
|
package/llms.txt
CHANGED
|
@@ -55,7 +55,7 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
55
55
|
- Vitals report maximum: 90 complete UTC days; `vitals` is an optional capability that hosts before `alpha.6` do not advertise.
|
|
56
56
|
- Site map maximum: 90 complete UTC days, read newest day first within 8,000 route rows; older days it could not read are declared as partial coverage. `siteMap` is an optional capability that hosts before `alpha.8` do not advertise, and its data starts at the first ingest after that upgrade.
|
|
57
57
|
- Maximum live visitors: 500.
|
|
58
|
-
- Live window:
|
|
58
|
+
- Live window: a `leave` beacon on pagehide retires a visitor about ten seconds after they leave, and a bfcache return revives them; the safety net without a goodbye is 90 seconds since the last event or heartbeat. `POST /rastro/events` re-anchors a batch whose `sentAt` is more than 30 seconds from server time, so a wrong device clock decides neither liveness nor hour buckets.
|
|
59
59
|
- Maximum journey events: 500.
|
|
60
60
|
- Token lifetime: 10 minutes, RS256, permission `analytics:read`.
|
|
61
61
|
|
package/package.json
CHANGED
|
@@ -207,6 +207,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
207
207
|
| "custom"
|
|
208
208
|
| "conversion"
|
|
209
209
|
| "heartbeat"
|
|
210
|
+
| "leave"
|
|
210
211
|
| "outbound"
|
|
211
212
|
| "vital";
|
|
212
213
|
value?: number;
|
|
@@ -697,6 +698,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
697
698
|
| "custom"
|
|
698
699
|
| "conversion"
|
|
699
700
|
| "heartbeat"
|
|
701
|
+
| "leave"
|
|
700
702
|
| "outbound"
|
|
701
703
|
| "vital";
|
|
702
704
|
visitorId: string;
|
|
@@ -794,6 +796,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
794
796
|
| "custom"
|
|
795
797
|
| "conversion"
|
|
796
798
|
| "heartbeat"
|
|
799
|
+
| "leave"
|
|
797
800
|
| "outbound"
|
|
798
801
|
| "vital";
|
|
799
802
|
visitorId: string;
|
|
@@ -7,7 +7,8 @@ export const MAX_BYTES_PER_SESSION_WINDOW = 256 * 1024;
|
|
|
7
7
|
export const SITE_INGEST_SHARDS = 4_096;
|
|
8
8
|
export const MAX_SITE_EVENTS_PER_SHARD_WINDOW = 300;
|
|
9
9
|
export const MAX_SITE_BYTES_PER_SHARD_WINDOW = 512 * 1024;
|
|
10
|
-
export const LIVE_SESSION_TTL_MS =
|
|
10
|
+
export const LIVE_SESSION_TTL_MS = 90_000;
|
|
11
|
+
export const LIVE_LEAVE_GRACE_MS = 10_000;
|
|
11
12
|
export const LIVE_SWEEP_INTERVAL_MS = 5_000;
|
|
12
13
|
export const CLOCK_SKEW_TOLERANCE_MS = 30_000;
|
|
13
14
|
export const AGGREGATE_SHARDS = 128;
|
package/src/component/http.ts
CHANGED
package/src/component/ingest.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
HOURLY_AGGREGATE_SHARDS,
|
|
11
11
|
DIMENSION_SLOTS,
|
|
12
12
|
HOUR_MS,
|
|
13
|
+
LIVE_LEAVE_GRACE_MS,
|
|
13
14
|
LIVE_SESSION_TTL_MS,
|
|
14
15
|
LIVE_SWEEP_INTERVAL_MS,
|
|
15
16
|
MAX_BATCH_EVENTS,
|
|
@@ -315,7 +316,12 @@ export const ingestBatch = mutation({
|
|
|
315
316
|
const bucketStart = eventBucketStart(event.timestamp);
|
|
316
317
|
const groupKey = eventBatchKey(event.sessionId, bucketStart);
|
|
317
318
|
requestGroups.add(groupKey);
|
|
318
|
-
if (
|
|
319
|
+
if (
|
|
320
|
+
event.type === "heartbeat" ||
|
|
321
|
+
event.type === "vital" ||
|
|
322
|
+
event.type === "leave"
|
|
323
|
+
)
|
|
324
|
+
continue;
|
|
319
325
|
const eventIds = eventIdsByGroup.get(groupKey) ?? new Set<string>();
|
|
320
326
|
if (eventIds.has(event.eventId)) {
|
|
321
327
|
duplicateInBatch.add(eventIndex);
|
|
@@ -422,6 +428,50 @@ export const ingestBatch = mutation({
|
|
|
422
428
|
continue;
|
|
423
429
|
}
|
|
424
430
|
|
|
431
|
+
if (event.type === "leave") {
|
|
432
|
+
// A departure only shortens live presence: it stores no telemetry,
|
|
433
|
+
// touches no aggregates, and never extends anything, so a replayed
|
|
434
|
+
// or late leave is inert. The read side filters by expiresAt and the
|
|
435
|
+
// sweep deletes the row, so the dashboard sees the exit in seconds.
|
|
436
|
+
if (!existingSession) {
|
|
437
|
+
rejected += 1;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (
|
|
441
|
+
!(await consumeRateLimit(
|
|
442
|
+
ctx,
|
|
443
|
+
args.siteId,
|
|
444
|
+
event.sessionId,
|
|
445
|
+
eventByteCounts[eventIndex],
|
|
446
|
+
now,
|
|
447
|
+
sessionRateWindows,
|
|
448
|
+
))
|
|
449
|
+
) {
|
|
450
|
+
rejected += 1;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const liveState = await loadLiveSession(
|
|
454
|
+
ctx,
|
|
455
|
+
args.siteId,
|
|
456
|
+
event.sessionId,
|
|
457
|
+
batchState,
|
|
458
|
+
);
|
|
459
|
+
const liveSession = liveState.current;
|
|
460
|
+
if (liveSession) {
|
|
461
|
+
const expiresAt = Math.min(
|
|
462
|
+
liveSession.expiresAt,
|
|
463
|
+
now + LIVE_LEAVE_GRACE_MS,
|
|
464
|
+
);
|
|
465
|
+
if (expiresAt < liveSession.expiresAt) {
|
|
466
|
+
await ctx.db.patch("liveSessions", liveSession._id, { expiresAt });
|
|
467
|
+
liveState.current = { ...liveSession, expiresAt };
|
|
468
|
+
}
|
|
469
|
+
await ensureLiveSweep(ctx, args.siteId, now, batchState);
|
|
470
|
+
}
|
|
471
|
+
accepted += 1;
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
|
|
425
475
|
if (event.type === "vital") {
|
|
426
476
|
// A vital annotates telemetry its session already produced; it never
|
|
427
477
|
// creates sessions, live presence, event batches, or aggregate counts.
|