@iann29/rastro 0.1.0-alpha.7 → 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 +60 -32
- package/agent/integration.md +14 -6
- package/agent/manifest.json +9 -7
- package/agent/manifest.schema.json +8 -5
- package/dist/client/federation.d.ts +13 -8
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +4 -1
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +419 -17
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +55 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +71 -7
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/constants.d.ts +6 -1
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +13 -1
- package/dist/component/constants.js.map +1 -1
- package/dist/component/coverage.d.ts +1 -0
- package/dist/component/coverage.d.ts.map +1 -1
- package/dist/component/coverage.js +13 -2
- package/dist/component/coverage.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 +2 -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 +173 -3
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/reports.d.ts +77 -3
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +275 -3
- package/dist/component/reports.js.map +1 -1
- package/dist/component/retention.d.ts +4 -4
- package/dist/component/retention.d.ts.map +1 -1
- package/dist/component/retention.js +12 -2
- package/dist/component/retention.js.map +1 -1
- package/dist/component/sanitize.d.ts +9 -0
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +25 -0
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +69 -7
- package/dist/component/schema.js +25 -0
- package/dist/component/schema.js.map +1 -1
- 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/federation-setup.md +2 -0
- package/docs/federation.md +4 -0
- package/docs/upgrading.md +91 -10
- package/llms.txt +3 -1
- package/package.json +1 -1
- package/src/component/_generated/component.ts +58 -1
- package/src/component/constants.ts +13 -1
- package/src/component/coverage.ts +12 -1
- package/src/component/http.ts +2 -0
- package/src/component/ingest.ts +278 -1
- package/src/component/reports.ts +364 -2
- package/src/component/retention.ts +16 -1
- package/src/component/sanitize.ts +31 -0
- package/src/component/schema.ts +26 -0
- 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/federation-setup.md
CHANGED
package/docs/federation.md
CHANGED
|
@@ -48,6 +48,8 @@ The exact generated surface is:
|
|
|
48
48
|
- `funnelsReport`
|
|
49
49
|
- `affiliatesReport`
|
|
50
50
|
- `dataCoverage`
|
|
51
|
+
- `vitalsReport` (optional capability `vitals`, hosts from `alpha.6`)
|
|
52
|
+
- `siteMap` (optional capability `siteMap`, hosts from `alpha.8`)
|
|
51
53
|
|
|
52
54
|
There are no ingestion, configuration-write, retention-write, raw-site, or site
|
|
53
55
|
mutation functions in this surface. The read-only goal, funnel, and affiliate
|
|
@@ -185,6 +187,8 @@ export const {
|
|
|
185
187
|
goalsReport,
|
|
186
188
|
funnelsReport,
|
|
187
189
|
affiliatesReport,
|
|
190
|
+
vitalsReport,
|
|
191
|
+
siteMap,
|
|
188
192
|
dataCoverage,
|
|
189
193
|
} = federated;
|
|
190
194
|
```
|
package/docs/upgrading.md
CHANGED
|
@@ -6,16 +6,18 @@ deployment.
|
|
|
6
6
|
|
|
7
7
|
## Release matrix
|
|
8
8
|
|
|
9
|
-
| Current state | Required path
|
|
10
|
-
| ----------------------------------------------------- |
|
|
11
|
-
| Fresh deployment | Install the intended release directly
|
|
12
|
-
| `0.1.0-alpha.0` with maintained data | Install `0.1.0-alpha.1` first and wait for staged visitor-index backfills
|
|
13
|
-
| `0.1.0-alpha.1` | Wait for staged visitor-index backfills, then install `alpha.2`
|
|
14
|
-
| `0.1.0-alpha.2` with legacy `events` rows | Stay on `alpha.2` and run/resume migration
|
|
15
|
-
| `0.1.0-alpha.3` fresh/canonical deployment | Rehearse the next alpha normally
|
|
16
|
-
| `0.1.0-alpha.3` through `alpha.5` with canonical data | Install `0.1.0-alpha.6` directly; Web Vitals tables are additive
|
|
17
|
-
| `0.1.0-alpha.6` | Install `0.1.0-alpha.7` directly; no schema change
|
|
18
|
-
|
|
|
9
|
+
| Current state | Required path | Success condition |
|
|
10
|
+
| ----------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
11
|
+
| Fresh deployment | Install the intended release directly | Component deploys and `/rastro/health` succeeds |
|
|
12
|
+
| `0.1.0-alpha.0` with maintained data | Install `0.1.0-alpha.1` first and wait for staged visitor-index backfills | Every staged index reports complete before installing `alpha.2` |
|
|
13
|
+
| `0.1.0-alpha.1` | Wait for staged visitor-index backfills, then install `alpha.2` | Backfills complete before active readers deploy |
|
|
14
|
+
| `0.1.0-alpha.2` with legacy `events` rows | Stay on `alpha.2` and run/resume migration | Durable success plus empty source |
|
|
15
|
+
| `0.1.0-alpha.3` fresh/canonical deployment | Rehearse the next alpha normally | Schema, reports, tracker, and retention checks pass |
|
|
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
|
+
| `0.1.0-alpha.6` | Install `0.1.0-alpha.7` directly; no schema change | `GET /rastro/tracker.js` contains `sentAt` |
|
|
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"` |
|
|
20
|
+
| Any release whose packed declarations lack federation | Stop and request an eligible exact registry release | Packed declarations contain the federation exports |
|
|
19
21
|
|
|
20
22
|
Do not upgrade a populated legacy deployment directly to a release that removes
|
|
21
23
|
the `events` table. Schema acceptance alone does not prove that telemetry was
|
|
@@ -129,6 +131,81 @@ Acceptance for this upgrade: `/rastro/health` succeeds, `GET /rastro/tracker.js`
|
|
|
129
131
|
contains `sentAt`, and a visitor whose device clock is minutes off appears in
|
|
130
132
|
**Ao vivo** within seconds of loading a page.
|
|
131
133
|
|
|
134
|
+
## Site map (alpha.8)
|
|
135
|
+
|
|
136
|
+
`0.1.0-alpha.8` adds the route map as a purely additive upgrade: a new
|
|
137
|
+
`routeDailyRollups` component table fed at ingestion, optional fields on
|
|
138
|
+
existing tables (`liveSessions.previousPath`/`movedAt`,
|
|
139
|
+
`sessions.lastPageviewAt`, `analyticsCoverage.siteMapSince`), the `siteMap`
|
|
140
|
+
query, and the retention kind `siteMapRollups` (governed by `reportsDays`). No
|
|
141
|
+
data migration and no staged indexes are involved — install and deploy through
|
|
142
|
+
the host's normal workflow. Ingestion writes one extra rollup document per
|
|
143
|
+
distinct route in a batch.
|
|
144
|
+
|
|
145
|
+
One federation edit is required. The connector manifest is static package
|
|
146
|
+
metadata: after this upgrade it advertises the optional `siteMap` capability
|
|
147
|
+
whether or not the host module exports the function, and connection verification
|
|
148
|
+
checks the manifest's shape, not each function's existence. A host that upgrades
|
|
149
|
+
the package without re-exporting the query leaves the dashboard's **Mapa** view
|
|
150
|
+
failing with a missing-function error. Add `siteMap` to the destructured export
|
|
151
|
+
in `convex/rastroFederation.ts` before deploying:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
export const {
|
|
155
|
+
manifest,
|
|
156
|
+
// ...the existing exports...
|
|
157
|
+
vitalsReport,
|
|
158
|
+
siteMap,
|
|
159
|
+
dataCoverage,
|
|
160
|
+
} = federated;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The map has no history to backfill: coverage starts at `siteMapSince`, stamped
|
|
164
|
+
by the first ingest after the upgrade, and the dashboard names the date its map
|
|
165
|
+
data begins. In the central dashboard, open **Conexões** and click **Verificar
|
|
166
|
+
novamente** on the active connection so the stored capability list picks up
|
|
167
|
+
`siteMap` and the **Mapa** view replaces its upgrade notice.
|
|
168
|
+
|
|
169
|
+
Acceptance for this upgrade: `/rastro/health` reports
|
|
170
|
+
`features.siteMap: "dailyRollup"`, and after one real visit that navigates
|
|
171
|
+
between two pages, `siteMap` returns both routes and the transition between
|
|
172
|
+
them.
|
|
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
|
+
|
|
132
209
|
## Federation releases
|
|
133
210
|
|
|
134
211
|
Federation is additive to component telemetry but requires host tables,
|
|
@@ -158,6 +235,10 @@ control-plane issuer.
|
|
|
158
235
|
- A real tracker request is accepted from an allowed origin.
|
|
159
236
|
- When the host opts into Web Vitals: `/rastro/vitals.js` serves and
|
|
160
237
|
`vitalsReport` reflects a real page load.
|
|
238
|
+
- On `alpha.8` or later: the federation module re-exports `siteMap` and, after a
|
|
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.
|
|
161
242
|
- Protected overview, journey, goal, funnel, and affiliate reports used by the
|
|
162
243
|
host still run.
|
|
163
244
|
- Trusted conversion idempotency and currency checks still pass.
|
package/llms.txt
CHANGED
|
@@ -53,8 +53,9 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
53
53
|
- Funnel report maximum: 90 complete UTC days.
|
|
54
54
|
- Affiliate reports require complete UTC-day boundaries.
|
|
55
55
|
- Vitals report maximum: 90 complete UTC days; `vitals` is an optional capability that hosts before `alpha.6` do not advertise.
|
|
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.
|
|
56
57
|
- Maximum live visitors: 500.
|
|
57
|
-
- 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.
|
|
58
59
|
- Maximum journey events: 500.
|
|
59
60
|
- Token lifetime: 10 minutes, RS256, permission `analytics:read`.
|
|
60
61
|
|
|
@@ -67,3 +68,4 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
67
68
|
- Overview ranges require complete UTC hour or day buckets. Manual pagination honors `numItems` and `cursor` only.
|
|
68
69
|
- `dataCoverage` echoes the authorized site ID but returns no event, visitor, or session IDs.
|
|
69
70
|
- `vitalsReport` returns p75, averages, and exact good/needs-improvement/poor counts per metric, page, and device — never individual measurements.
|
|
71
|
+
- `siteMap` returns per-route totals (pageviews, estimated visitors, entries, derived exits and bounces, time on page) and route-to-route transition counts, with numeric, UUID, and long hexadecimal segments collapsed to `:id` — never sessions, visitor IDs, or query strings. `liveVisitors` adds `previousPath` and `movedAt` when a visitor moves between routes.
|
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;
|
|
@@ -306,7 +307,8 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
306
307
|
| "goals"
|
|
307
308
|
| "funnels"
|
|
308
309
|
| "affiliates"
|
|
309
|
-
| "vitals"
|
|
310
|
+
| "vitals"
|
|
311
|
+
| "siteMap";
|
|
310
312
|
}>;
|
|
311
313
|
from: number;
|
|
312
314
|
siteId: string;
|
|
@@ -515,9 +517,11 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
515
517
|
lastSeenAt: number;
|
|
516
518
|
latitude?: number;
|
|
517
519
|
longitude?: number;
|
|
520
|
+
movedAt?: number;
|
|
518
521
|
os: string;
|
|
519
522
|
pageviewCount: number;
|
|
520
523
|
path: string;
|
|
524
|
+
previousPath?: string;
|
|
521
525
|
sessionId: string;
|
|
522
526
|
siteId: string;
|
|
523
527
|
source: string;
|
|
@@ -694,6 +698,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
694
698
|
| "custom"
|
|
695
699
|
| "conversion"
|
|
696
700
|
| "heartbeat"
|
|
701
|
+
| "leave"
|
|
697
702
|
| "outbound"
|
|
698
703
|
| "vital";
|
|
699
704
|
visitorId: string;
|
|
@@ -703,6 +708,54 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
703
708
|
},
|
|
704
709
|
Name
|
|
705
710
|
>;
|
|
711
|
+
siteMap: FunctionReference<
|
|
712
|
+
"query",
|
|
713
|
+
"internal",
|
|
714
|
+
{ from: number; siteIds: Array<string>; to: number },
|
|
715
|
+
{
|
|
716
|
+
from: number;
|
|
717
|
+
metadata: {
|
|
718
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
719
|
+
coverage: {
|
|
720
|
+
availableFrom: number | null;
|
|
721
|
+
availableThrough: number | null;
|
|
722
|
+
generation: number | null;
|
|
723
|
+
retainedBefore: number | null;
|
|
724
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
725
|
+
};
|
|
726
|
+
limits: {
|
|
727
|
+
routesPerDay: number;
|
|
728
|
+
rows: number;
|
|
729
|
+
transitionSlots: number;
|
|
730
|
+
};
|
|
731
|
+
range: {
|
|
732
|
+
boundary: "inclusive";
|
|
733
|
+
from: number;
|
|
734
|
+
timezone: "UTC";
|
|
735
|
+
to: number;
|
|
736
|
+
};
|
|
737
|
+
visitors: { basis: "estimatedUnique"; exact: boolean };
|
|
738
|
+
};
|
|
739
|
+
routes: Array<{
|
|
740
|
+
bounces: number;
|
|
741
|
+
daily: Array<{ bucketStart: number; pageviews: number }>;
|
|
742
|
+
dwellMs: number;
|
|
743
|
+
dwellSamples: number;
|
|
744
|
+
entries: number;
|
|
745
|
+
exits: number;
|
|
746
|
+
pageviews: number;
|
|
747
|
+
route: string;
|
|
748
|
+
transitionsIn: number;
|
|
749
|
+
transitionsOut: number;
|
|
750
|
+
visitors: number;
|
|
751
|
+
}>;
|
|
752
|
+
to: number;
|
|
753
|
+
transitions: Array<{ count: number; from: string; to: string }>;
|
|
754
|
+
truncated: { routes: boolean; transitions: boolean };
|
|
755
|
+
window: { from: number; to: number } | null;
|
|
756
|
+
},
|
|
757
|
+
Name
|
|
758
|
+
>;
|
|
706
759
|
visitorJourney: FunctionReference<
|
|
707
760
|
"query",
|
|
708
761
|
"internal",
|
|
@@ -743,6 +796,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
743
796
|
| "custom"
|
|
744
797
|
| "conversion"
|
|
745
798
|
| "heartbeat"
|
|
799
|
+
| "leave"
|
|
746
800
|
| "outbound"
|
|
747
801
|
| "vital";
|
|
748
802
|
visitorId: string;
|
|
@@ -828,6 +882,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
828
882
|
| "funnelRollups"
|
|
829
883
|
| "affiliateRollups"
|
|
830
884
|
| "vitalRollups"
|
|
885
|
+
| "siteMapRollups"
|
|
831
886
|
| "attributions"
|
|
832
887
|
| "liveSessions"
|
|
833
888
|
| "ingestWindows"
|
|
@@ -863,6 +918,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
863
918
|
| "funnelRollups"
|
|
864
919
|
| "affiliateRollups"
|
|
865
920
|
| "vitalRollups"
|
|
921
|
+
| "siteMapRollups"
|
|
866
922
|
| "attributions"
|
|
867
923
|
| "liveSessions"
|
|
868
924
|
| "ingestWindows"
|
|
@@ -909,6 +965,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
909
965
|
| "funnelRollups"
|
|
910
966
|
| "affiliateRollups"
|
|
911
967
|
| "vitalRollups"
|
|
968
|
+
| "siteMapRollups"
|
|
912
969
|
| "attributions"
|
|
913
970
|
| "liveSessions"
|
|
914
971
|
| "ingestWindows"
|
|
@@ -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;
|
|
@@ -54,3 +55,14 @@ export const HOUR_MS = 3_600_000;
|
|
|
54
55
|
export const MINUTE_MS = 60_000;
|
|
55
56
|
export const RETENTION_POLICY_INTERVAL_MS = DAY_MS;
|
|
56
57
|
export const MAX_VISITOR_ALIASES = 10;
|
|
58
|
+
// Site map rollups: one row per (site, route, UTC day, shard). A day keeps at
|
|
59
|
+
// most this many route rows per shard before new routes fold into "(other)";
|
|
60
|
+
// each row keeps this many outgoing targets before they fold the same way. A
|
|
61
|
+
// report reads at most MAX_SITE_MAP_ROWS rows, newest day first — about
|
|
62
|
+
// 5 MiB of rows, under the 8 MiB a function may read once the feature-report
|
|
63
|
+
// byte reserve is kept — and declares the older days it could not read as
|
|
64
|
+
// partial coverage: 20 days at the daily cap, 40 for a 100-route site.
|
|
65
|
+
export const MAX_SITE_MAP_ROUTES_PER_DAY = 200;
|
|
66
|
+
export const ROUTE_TRANSITION_SLOTS = 24;
|
|
67
|
+
export const MAX_SITE_MAP_ROWS = 8_000;
|
|
68
|
+
export const SITE_MAP_OTHER_ROUTE = "(other)";
|
|
@@ -12,11 +12,22 @@ export async function ensureAnalyticsControl(
|
|
|
12
12
|
.query("analyticsCoverage")
|
|
13
13
|
.withIndex("by_siteId", (range) => range.eq("siteId", siteId))
|
|
14
14
|
.unique();
|
|
15
|
-
if (existing)
|
|
15
|
+
if (existing) {
|
|
16
|
+
if (existing.siteMapSince !== undefined) return existing;
|
|
17
|
+
// A site that predates the site map starts its map coverage at the first
|
|
18
|
+
// ingest after the upgrade; the same safety delay applies as for rollups.
|
|
19
|
+
const siteMapSince = now + ROLLUP_SAFETY_DELAY_MS;
|
|
20
|
+
await ctx.db.patch("analyticsCoverage", existing._id, {
|
|
21
|
+
siteMapSince,
|
|
22
|
+
updatedAt: now,
|
|
23
|
+
});
|
|
24
|
+
return { ...existing, siteMapSince, updatedAt: now };
|
|
25
|
+
}
|
|
16
26
|
const fields = {
|
|
17
27
|
siteId,
|
|
18
28
|
generation: 1 as const,
|
|
19
29
|
rollupCompleteFrom: now + ROLLUP_SAFETY_DELAY_MS,
|
|
30
|
+
siteMapSince: now + ROLLUP_SAFETY_DELAY_MS,
|
|
20
31
|
retained,
|
|
21
32
|
updatedAt: now,
|
|
22
33
|
};
|