@iann29/rastro 0.1.0-alpha.2 → 0.1.0-alpha.3
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 +79 -30
- package/dist/component/_generated/api.d.ts +5 -5
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +10 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +4 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/convex.config.d.ts +7 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/convex.config.js +9 -4
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/diagnostics.d.ts +9 -0
- package/dist/component/diagnostics.d.ts.map +1 -0
- package/dist/component/diagnostics.js +47 -0
- package/dist/component/diagnostics.js.map +1 -0
- package/dist/component/eventStore.d.ts +11 -9
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/eventStore.js +55 -227
- package/dist/component/eventStore.js.map +1 -1
- package/dist/component/geo.d.ts +71 -0
- package/dist/component/geo.d.ts.map +1 -0
- package/dist/component/geo.js +610 -0
- package/dist/component/geo.js.map +1 -0
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +124 -11
- package/dist/component/http.js.map +1 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +34 -14
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/reports.d.ts +8 -8
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +2 -1
- package/dist/component/reports.js.map +1 -1
- package/dist/component/schema.d.ts +15 -52
- package/dist/component/schema.js +14 -16
- package/dist/component/schema.js.map +1 -1
- package/dist/component/validators.d.ts +3 -50
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +4 -12
- 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.js +7 -6
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker.min.js +1 -1
- package/docs/benchmarks/2026-08-20-realistic.md +1 -1
- package/package.json +2 -6
- package/scripts/benchmark-ingest.mjs +81 -32
- package/src/component/_generated/api.ts +5 -5
- package/src/component/_generated/component.ts +13 -0
- package/src/component/_generated/server.ts +4 -0
- package/src/component/convex.config.ts +11 -5
- package/src/component/diagnostics.ts +64 -0
- package/src/component/eventStore.ts +78 -280
- package/src/component/geo.ts +779 -0
- package/src/component/http.ts +184 -17
- package/src/component/ingest.ts +49 -15
- package/src/component/reports.ts +4 -1
- package/src/component/schema.ts +15 -16
- package/src/component/validators.ts +4 -13
- package/src/test.ts +0 -2
- package/dist/component/migrations.d.ts +0 -17
- package/dist/component/migrations.d.ts.map +0 -1
- package/dist/component/migrations.js +0 -44
- package/dist/component/migrations.js.map +0 -1
- package/src/component/migrations.ts +0 -52
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const TRACKER_SOURCE = "
|
|
2
|
-
export declare const TRACKER_GZIP_BASE64 = "
|
|
3
|
-
export declare const TRACKER_RAW_BYTES =
|
|
4
|
-
export declare const TRACKER_GZIP_BYTES =
|
|
1
|
+
export declare 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 n=()=>crypto.randomUUID?.()||Math.random()+\"\",o=new URLSearchParams(location.search).get(\"ref\")?.slice(0,64);let r,a;try{r=sessionStorage._r||n(),sessionStorage._r=r,a=o||sessionStorage._a,a&&(sessionStorage._a=a)}catch{r=n(),a=o}const i=e.dataset.visitor||r;let c,d=0;const l=[],h=document.referrer?new URL(document.referrer).origin:void 0,p=e=>{const n=l.splice(0,50);if(!n.length)return;const o=()=>new Blob([JSON.stringify({siteId:t,events:n})]);let r=o();for(;r.size>64e3&&n.length>1;)l.unshift(n.pop()),r=o();e&&navigator.sendBeacon(s,r)||fetch(s,{method:\"POST\",body:r,keepalive:1}).catch(()=>{}),l.length&&p(e)},g=(e,t,s)=>{const n=Date.now();l.push({eventId:`${r}.${n.toString(36)}.${d}`,sessionId:r,visitorId:i,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:n,sequence:d++,affiliateSlug:a,...s}),clearTimeout(c),c=setTimeout(p,1200)};let u;const v=()=>{const e=location.pathname;e!==u&&(u=e,g(\"pageview\"))};window.rastro=(e,t,s)=>{if(\"context\"===e)return{sessionId:r,visitorId:i};\"event\"===e&&g(\"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,n=t.href,o=n&&new URL(n),r=/^https?:$/.test(o?.protocol);g(s?\"custom\":r&&o.origin!==location.origin?\"outbound\":\"click\",s,{target:(t.dataset.rastroLabel||t.tagName).slice(0,80),href:r?o.pathname.slice(0,256):void 0})},1);const m=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),v()}};m(\"pushState\"),m(\"replaceState\"),addEventListener(\"popstate\",v),addEventListener(\"pagehide\",()=>p(1)),setInterval(()=>{g(\"heartbeat\"),p()},2e4),v()})();";
|
|
2
|
+
export declare const TRACKER_GZIP_BASE64 = "H4sIAAAAAAACA3VUbW/bNhD+K4kQCCRyZZ00DQYJjIGi+5Aha4u5/RRkC0OdJaISyZEnZ56l/z7oxU7SdN+s52Td3fNyjHF5tdPORjpCWTjdNmhJ6DYEtLTSwXgCkrgUhSIVkUQ0hBCfI2gL74ylrsOliEGnqcXHo29/3LAEN2gpJoADzkUVcJ2bNTumrjuOPCC1weZTdyuHUXTYenIiKFu45tu3649LwXjX/a6omkHGT5MEnJx7rFAFXX1RQTWR1U4rMs6KOKJclEgsCbhO+FLE2mhkC7i84HmNdBRA5RS2uyAjxmicXZELqkTxV+g6yzi8gmUAJV3X/VhQoNKUvUKl4r1WpKtdkMP3lHT9tKuReGBvY6IhF7oujFNpKORipqSWt3dQPakScI0hYFju+X1V4cIFUxqbbZwpjhbgJR7ktbIW0c8kvF/wUQgrarQlVS+1cKMWQ5cPtXtgt7+tPn8SkYKxpVlv2W7wwHWREUz6ZrbndzOp0jGer11geRDR/ItXlxf4Lk33fa7Ocl6L1sbKrIlZ4Z1nnMP0N0xTqzamVOSCiGiLD6i0syxC4F23RtIVi7BrkCpXZMmXz6uvCTy4YpsF+I7oVW02mJ31XIy0s9HbPYd6bp6mniHvoZQMgSDyZ9x8VITCukfG81r4NlZsNy53XWT3J7vQi5OdFeRWIwns3SUfkKK/37vkusgCzFpeF5kB2nrMEKxqMCPwiqrsYM/haSgcPHn+/pLDXsSsAjINRlKNzyxE/LtFqzErTk9BrdemNopwVbdlpkAIEXsOukYVvpoGXUtMc9AyIu2fPZydLxa8HwVqZ4k38kXyX42W47GUbZqyViKULPGqxI3Bx4TzPn80tnCPIqhIwT0j06xZop0l/IcSKSXOrtr9D0d9Ph2I8d00LVmi20iuSeD+ZEddN1f7+wNPvyw4xDT9wY+R7911jhfLyfrZzgfnMZDBmMWe93AIiyqKX4cP35hIaDGwRNdGf0/gKSskUZAKJdJS6NpFjMQSBQ8tkbNwO2T3zbT9m3HGu2SKE73MUZR0yPn0+tgYrKTxFg5n7Ola2iEHb/+siHxcZidvBQ1t3VL44MhpV/O8ZHG5JykLaermvB/LJwUnZJm4lh5ca4sk2+8XYTctlbEf57pRD1h3HQlS5SfVIH9B+TBrFpbu58adj83A8RmfN2/kczIrE8mF7S3e5U8/JRNC4GAbEsr7esvmGiCHDeN9nzcsGaK4IkWYcGiGW+5rpXGPvJbSOx/HImx+WlYlVqbABAb7e3bGhztP15YwbFQ9nYySJRWqQA+oKOHgGe/hHC+mqTjj+X/ssrhNNgcAAA==";
|
|
3
|
+
export declare const TRACKER_RAW_BYTES = 1846;
|
|
4
|
+
export declare const TRACKER_GZIP_BYTES = 1000;
|
|
5
5
|
//# sourceMappingURL=generated.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../src/tracker/generated.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../src/tracker/generated.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,i2DAAi2D,CAAC;AAC73D,eAAO,MAAM,mBAAmB,6zCAA6zC,CAAC;AAC91C,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,kBAAkB,OAAO,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by scripts/build-tracker.mjs. Do not edit.
|
|
2
|
-
export const TRACKER_SOURCE = "
|
|
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 n=()=>crypto.randomUUID?.()||Math.random()+\"\",o=new URLSearchParams(location.search).get(\"ref\")?.slice(0,64);let r,a;try{r=sessionStorage._r||n(),sessionStorage._r=r,a=o||sessionStorage._a,a&&(sessionStorage._a=a)}catch{r=n(),a=o}const i=e.dataset.visitor||r;let c,d=0;const l=[],h=document.referrer?new URL(document.referrer).origin:void 0,p=e=>{const n=l.splice(0,50);if(!n.length)return;const o=()=>new Blob([JSON.stringify({siteId:t,events:n})]);let r=o();for(;r.size>64e3&&n.length>1;)l.unshift(n.pop()),r=o();e&&navigator.sendBeacon(s,r)||fetch(s,{method:\"POST\",body:r,keepalive:1}).catch(()=>{}),l.length&&p(e)},g=(e,t,s)=>{const n=Date.now();l.push({eventId:`${r}.${n.toString(36)}.${d}`,sessionId:r,visitorId:i,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:n,sequence:d++,affiliateSlug:a,...s}),clearTimeout(c),c=setTimeout(p,1200)};let u;const v=()=>{const e=location.pathname;e!==u&&(u=e,g(\"pageview\"))};window.rastro=(e,t,s)=>{if(\"context\"===e)return{sessionId:r,visitorId:i};\"event\"===e&&g(\"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,n=t.href,o=n&&new URL(n),r=/^https?:$/.test(o?.protocol);g(s?\"custom\":r&&o.origin!==location.origin?\"outbound\":\"click\",s,{target:(t.dataset.rastroLabel||t.tagName).slice(0,80),href:r?o.pathname.slice(0,256):void 0})},1);const m=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),v()}};m(\"pushState\"),m(\"replaceState\"),addEventListener(\"popstate\",v),addEventListener(\"pagehide\",()=>p(1)),setInterval(()=>{g(\"heartbeat\"),p()},2e4),v()})();";
|
|
3
|
+
export const TRACKER_GZIP_BASE64 = "H4sIAAAAAAACA3VUbW/bNhD+K4kQCCRyZZ00DQYJjIGi+5Aha4u5/RRkC0OdJaISyZEnZ56l/z7oxU7SdN+s52Td3fNyjHF5tdPORjpCWTjdNmhJ6DYEtLTSwXgCkrgUhSIVkUQ0hBCfI2gL74ylrsOliEGnqcXHo29/3LAEN2gpJoADzkUVcJ2bNTumrjuOPCC1weZTdyuHUXTYenIiKFu45tu3649LwXjX/a6omkHGT5MEnJx7rFAFXX1RQTWR1U4rMs6KOKJclEgsCbhO+FLE2mhkC7i84HmNdBRA5RS2uyAjxmicXZELqkTxV+g6yzi8gmUAJV3X/VhQoNKUvUKl4r1WpKtdkMP3lHT9tKuReGBvY6IhF7oujFNpKORipqSWt3dQPakScI0hYFju+X1V4cIFUxqbbZwpjhbgJR7ktbIW0c8kvF/wUQgrarQlVS+1cKMWQ5cPtXtgt7+tPn8SkYKxpVlv2W7wwHWREUz6ZrbndzOp0jGer11geRDR/ItXlxf4Lk33fa7Ocl6L1sbKrIlZ4Z1nnMP0N0xTqzamVOSCiGiLD6i0syxC4F23RtIVi7BrkCpXZMmXz6uvCTy4YpsF+I7oVW02mJ31XIy0s9HbPYd6bp6mniHvoZQMgSDyZ9x8VITCukfG81r4NlZsNy53XWT3J7vQi5OdFeRWIwns3SUfkKK/37vkusgCzFpeF5kB2nrMEKxqMCPwiqrsYM/haSgcPHn+/pLDXsSsAjINRlKNzyxE/LtFqzErTk9BrdemNopwVbdlpkAIEXsOukYVvpoGXUtMc9AyIu2fPZydLxa8HwVqZ4k38kXyX42W47GUbZqyViKULPGqxI3Bx4TzPn80tnCPIqhIwT0j06xZop0l/IcSKSXOrtr9D0d9Ph2I8d00LVmi20iuSeD+ZEddN1f7+wNPvyw4xDT9wY+R7911jhfLyfrZzgfnMZDBmMWe93AIiyqKX4cP35hIaDGwRNdGf0/gKSskUZAKJdJS6NpFjMQSBQ8tkbNwO2T3zbT9m3HGu2SKE73MUZR0yPn0+tgYrKTxFg5n7Ola2iEHb/+siHxcZidvBQ1t3VL44MhpV/O8ZHG5JykLaermvB/LJwUnZJm4lh5ca4sk2+8XYTctlbEf57pRD1h3HQlS5SfVIH9B+TBrFpbu58adj83A8RmfN2/kczIrE8mF7S3e5U8/JRNC4GAbEsr7esvmGiCHDeN9nzcsGaK4IkWYcGiGW+5rpXGPvJbSOx/HImx+WlYlVqbABAb7e3bGhztP15YwbFQ9nYySJRWqQA+oKOHgGe/hHC+mqTjj+X/ssrhNNgcAAA==";
|
|
4
|
+
export const TRACKER_RAW_BYTES = 1846;
|
|
5
|
+
export const TRACKER_GZIP_BYTES = 1000;
|
|
6
6
|
//# sourceMappingURL=generated.js.map
|
|
@@ -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,81DAA81D,CAAC;AAC73D,MAAM,CAAC,MAAM,mBAAmB,GAAG,0zCAA0zC,CAAC;AAC91C,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC"}
|
package/dist/tracker/tracker.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
(() => {
|
|
3
3
|
const script = document.currentScript;
|
|
4
4
|
const siteId = script?.dataset.site;
|
|
5
|
-
const endpoint = script?.dataset.endpoint ||
|
|
5
|
+
const endpoint = script?.dataset.endpoint ||
|
|
6
|
+
(script?.src && new URL("events", script.src).href);
|
|
6
7
|
if (!siteId || !endpoint)
|
|
7
8
|
return;
|
|
8
9
|
const createId = () => crypto.randomUUID?.() || Math.random() + "";
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
const emit = (type, name, extra) => {
|
|
51
52
|
const timestamp = Date.now();
|
|
52
53
|
queue.push({
|
|
53
|
-
eventId: `${sessionId}.${timestamp.toString(36)}.${sequence
|
|
54
|
+
eventId: `${sessionId}.${timestamp.toString(36)}.${sequence}`,
|
|
54
55
|
sessionId,
|
|
55
56
|
visitorId,
|
|
56
57
|
type,
|
|
@@ -88,8 +89,8 @@
|
|
|
88
89
|
return;
|
|
89
90
|
const name = element.dataset.rastroEvent;
|
|
90
91
|
const href = element.href;
|
|
91
|
-
const url = href
|
|
92
|
-
const web = url?.protocol
|
|
92
|
+
const url = (href && new URL(href));
|
|
93
|
+
const web = /^https?:$/.test(url?.protocol);
|
|
93
94
|
emit(name
|
|
94
95
|
? "custom"
|
|
95
96
|
: web && url.origin !== location.origin
|
|
@@ -101,8 +102,8 @@
|
|
|
101
102
|
}, true);
|
|
102
103
|
const wrapHistory = (method) => {
|
|
103
104
|
const native = history[method];
|
|
104
|
-
history[method] = function (
|
|
105
|
-
native.
|
|
105
|
+
history[method] = function (...args) {
|
|
106
|
+
native.apply(history, args);
|
|
106
107
|
pageview();
|
|
107
108
|
};
|
|
108
109
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/tracker/tracker.ts"],"names":[],"mappings":";AAyCA,CAAC,GAAG,EAAE;IACJ,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAyC,CAAC;IAClE,MAAM,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,EAAE,OAAO,CAAC,QAAQ,
|
|
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
|
-
|
|
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 n=()=>crypto.randomUUID?.()||Math.random()+"",o=new URLSearchParams(location.search).get("ref")?.slice(0,64);let r,a;try{r=sessionStorage._r||n(),sessionStorage._r=r,a=o||sessionStorage._a,a&&(sessionStorage._a=a)}catch{r=n(),a=o}const i=e.dataset.visitor||r;let c,d=0;const l=[],h=document.referrer?new URL(document.referrer).origin:void 0,p=e=>{const n=l.splice(0,50);if(!n.length)return;const o=()=>new Blob([JSON.stringify({siteId:t,events:n})]);let r=o();for(;r.size>64e3&&n.length>1;)l.unshift(n.pop()),r=o();e&&navigator.sendBeacon(s,r)||fetch(s,{method:"POST",body:r,keepalive:1}).catch(()=>{}),l.length&&p(e)},g=(e,t,s)=>{const n=Date.now();l.push({eventId:`${r}.${n.toString(36)}.${d}`,sessionId:r,visitorId:i,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:n,sequence:d++,affiliateSlug:a,...s}),clearTimeout(c),c=setTimeout(p,1200)};let u;const v=()=>{const e=location.pathname;e!==u&&(u=e,g("pageview"))};window.rastro=(e,t,s)=>{if("context"===e)return{sessionId:r,visitorId:i};"event"===e&&g("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,n=t.href,o=n&&new URL(n),r=/^https?:$/.test(o?.protocol);g(s?"custom":r&&o.origin!==location.origin?"outbound":"click",s,{target:(t.dataset.rastroLabel||t.tagName).slice(0,80),href:r?o.pathname.slice(0,256):void 0})},1);const m=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),v()}};m("pushState"),m("replaceState"),addEventListener("popstate",v),addEventListener("pagehide",()=>p(1)),setInterval(()=>{g("heartbeat"),p()},2e4),v()})();
|
|
@@ -150,7 +150,7 @@ A passing harness certification must include:
|
|
|
150
150
|
- `certification.requested: true` and `certification.passed: true`;
|
|
151
151
|
- zero request failures, duplicate events, rejected events, and failed events;
|
|
152
152
|
- completed request accounting;
|
|
153
|
-
- reliable and exact
|
|
153
|
+
- reliable and exact run-session reconciliation;
|
|
154
154
|
- separate accepted and stored throughput/capacity fields.
|
|
155
155
|
|
|
156
156
|
Release evidence should additionally record deployment health and successful
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/amageweb/amage-rastro/issues"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.3",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -123,9 +123,5 @@
|
|
|
123
123
|
"vitest": "4.1.10"
|
|
124
124
|
},
|
|
125
125
|
"types": "./dist/client/index.d.ts",
|
|
126
|
-
"module": "./dist/client/index.js"
|
|
127
|
-
"dependencies": {
|
|
128
|
-
"@convex-dev/migrations": "^0.3.6",
|
|
129
|
-
"convex-helpers": "^0.1.123"
|
|
130
|
-
}
|
|
126
|
+
"module": "./dist/client/index.js"
|
|
131
127
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { performance } from "node:perf_hooks";
|
|
4
5
|
import { clearInterval, setInterval } from "node:timers";
|
|
5
6
|
import { ConvexHttpClient } from "convex/browser";
|
|
6
7
|
import { makeFunctionReference } from "convex/server";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
+
const reconcileSessionsQuery = makeFunctionReference(
|
|
10
|
+
"benchmark:reconcileSessionsPage",
|
|
11
|
+
);
|
|
9
12
|
|
|
10
13
|
const PROFILE_DEFAULTS = {
|
|
11
14
|
lean: { batchSize: 50, sessions: 0 },
|
|
@@ -200,6 +203,14 @@ function percentile(sorted, fraction) {
|
|
|
200
203
|
return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * fraction) - 1)];
|
|
201
204
|
}
|
|
202
205
|
|
|
206
|
+
function hasValidSessionTotals(value) {
|
|
207
|
+
return value !== null &&
|
|
208
|
+
typeof value === "object" &&
|
|
209
|
+
[value.requestedSessions, value.foundSessions, value.storedEvents].every(
|
|
210
|
+
(total) => Number.isSafeInteger(total) && total >= 0,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
203
214
|
async function main() {
|
|
204
215
|
const options = parseOptions(process.argv.slice(2));
|
|
205
216
|
if (options.batchSize > 50) throw new Error("--batch-size cannot exceed 50");
|
|
@@ -212,7 +223,7 @@ async function main() {
|
|
|
212
223
|
if (options.certify && Number.isFinite(options.maximumRequests)) {
|
|
213
224
|
throw new Error("--certify cannot be combined with --requests");
|
|
214
225
|
}
|
|
215
|
-
const runId =
|
|
226
|
+
const runId = randomUUID();
|
|
216
227
|
const sessions = Array.from({ length: options.sessionCount }, (_, index) => ({
|
|
217
228
|
id: `bench-${runId}-${index}`,
|
|
218
229
|
visitorId: `visitor-${runId}-${index}`,
|
|
@@ -288,20 +299,23 @@ async function main() {
|
|
|
288
299
|
process.once("SIGINT", stopForInterrupt);
|
|
289
300
|
process.once("SIGTERM", stopForTermination);
|
|
290
301
|
|
|
291
|
-
const wallClockStartedAt = Date.now();
|
|
292
|
-
const reconciliationFrom = Math.floor(wallClockStartedAt / 3_600_000) * 3_600_000;
|
|
293
|
-
const reconciliationTo = Math.ceil(
|
|
294
|
-
(wallClockStartedAt + options.durationSeconds * 1_000 + 60_000) / 3_600_000,
|
|
295
|
-
) * 3_600_000 - 1;
|
|
296
302
|
const convex = options.convexUrl ? new ConvexHttpClient(options.convexUrl) : null;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
if (convex) {
|
|
304
|
+
const preflight = await convex.query(reconcileSessionsQuery, {
|
|
305
|
+
siteId: options.siteId,
|
|
306
|
+
runId,
|
|
307
|
+
start: 0,
|
|
308
|
+
count: 1,
|
|
309
|
+
});
|
|
310
|
+
if (
|
|
311
|
+
!hasValidSessionTotals(preflight) ||
|
|
312
|
+
preflight.requestedSessions !== 1 ||
|
|
313
|
+
preflight.foundSessions !== 0 ||
|
|
314
|
+
preflight.storedEvents !== 0
|
|
315
|
+
) {
|
|
316
|
+
throw new Error("reconciliation preflight returned unexpected totals");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
305
319
|
const startedAt = performance.now();
|
|
306
320
|
const deadline = startedAt + options.durationSeconds * 1_000;
|
|
307
321
|
const recordError = (key) => errors.set(key, (errors.get(key) ?? 0) + 1);
|
|
@@ -392,31 +406,66 @@ async function main() {
|
|
|
392
406
|
|
|
393
407
|
const elapsedMs = performance.now() - startedAt;
|
|
394
408
|
terminationReason ??= performance.now() >= deadline ? "duration" : "completed";
|
|
395
|
-
const aggregateAfter = convex
|
|
396
|
-
? await convex.query(overviewQuery, {
|
|
397
|
-
siteIds: [options.siteId],
|
|
398
|
-
from: reconciliationFrom,
|
|
399
|
-
to: reconciliationTo,
|
|
400
|
-
interval: "hour",
|
|
401
|
-
})
|
|
402
|
-
: null;
|
|
403
409
|
process.removeListener("SIGINT", stopForInterrupt);
|
|
404
410
|
process.removeListener("SIGTERM", stopForTermination);
|
|
405
411
|
const durationCompleted = terminationReason === "duration";
|
|
406
412
|
durations.sort((left, right) => left - right);
|
|
407
|
-
const
|
|
408
|
-
|
|
413
|
+
const expectedSessions = options.profile === "lean"
|
|
414
|
+
? nextRequest
|
|
415
|
+
: Math.min(nextRequest, options.sessionCount);
|
|
416
|
+
let sessionTotals = null;
|
|
417
|
+
if (convex && expectedSessions > 0) {
|
|
418
|
+
try {
|
|
419
|
+
sessionTotals = {
|
|
420
|
+
requestedSessions: 0,
|
|
421
|
+
foundSessions: 0,
|
|
422
|
+
storedEvents: 0,
|
|
423
|
+
};
|
|
424
|
+
for (let start = 0; start < expectedSessions; start += 100) {
|
|
425
|
+
const page = await convex.query(reconcileSessionsQuery, {
|
|
426
|
+
siteId: options.siteId,
|
|
427
|
+
runId,
|
|
428
|
+
start,
|
|
429
|
+
count: Math.min(100, expectedSessions - start),
|
|
430
|
+
});
|
|
431
|
+
if (!hasValidSessionTotals(page)) {
|
|
432
|
+
throw new Error("reconciliation response has invalid totals");
|
|
433
|
+
}
|
|
434
|
+
sessionTotals.requestedSessions += page.requestedSessions;
|
|
435
|
+
sessionTotals.foundSessions += page.foundSessions;
|
|
436
|
+
sessionTotals.storedEvents += page.storedEvents;
|
|
437
|
+
}
|
|
438
|
+
} catch (error) {
|
|
439
|
+
reconciliationReliable = false;
|
|
440
|
+
sessionTotals = null;
|
|
441
|
+
recordError(
|
|
442
|
+
`reconciliation:${error instanceof Error ? error.message : "unknown"}`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const expectedStoredEvents = reconciliationReliable
|
|
447
|
+
? accepted - acceptedHeartbeats
|
|
448
|
+
: null;
|
|
449
|
+
const storedEvents = sessionTotals?.storedEvents ?? expectedStoredEvents;
|
|
450
|
+
const reconciliation = sessionTotals
|
|
409
451
|
? {
|
|
410
|
-
scope: "
|
|
452
|
+
scope: "run-session-ids",
|
|
411
453
|
reliable: reconciliationReliable && failures === 0 && rejected === 0,
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
454
|
+
expectedSessions,
|
|
455
|
+
requestedSessions: sessionTotals.requestedSessions,
|
|
456
|
+
foundSessions: sessionTotals.foundSessions,
|
|
457
|
+
acceptedEvents: accepted,
|
|
458
|
+
acceptedHeartbeats: reconciliationReliable ? acceptedHeartbeats : null,
|
|
459
|
+
expectedStoredEvents,
|
|
460
|
+
storedEvents: sessionTotals.storedEvents,
|
|
415
461
|
matches:
|
|
416
462
|
reconciliationReliable &&
|
|
417
463
|
failures === 0 &&
|
|
418
464
|
rejected === 0 &&
|
|
419
|
-
|
|
465
|
+
expectedStoredEvents !== null &&
|
|
466
|
+
sessionTotals.requestedSessions === expectedSessions &&
|
|
467
|
+
sessionTotals.foundSessions === expectedSessions &&
|
|
468
|
+
sessionTotals.storedEvents === expectedStoredEvents,
|
|
420
469
|
}
|
|
421
470
|
: null;
|
|
422
471
|
const cleanRun =
|
|
@@ -458,13 +507,13 @@ async function main() {
|
|
|
458
507
|
errors: Object.fromEntries(errors),
|
|
459
508
|
throughput: {
|
|
460
509
|
eventsPerSecond: Math.round((accepted * 1_000) / elapsedMs),
|
|
461
|
-
storedEventsPerSecond:
|
|
510
|
+
storedEventsPerSecond: reconciliationPassed
|
|
462
511
|
? Math.round((storedEvents * 1_000) / elapsedMs)
|
|
463
512
|
: null,
|
|
464
513
|
estimatedEventsPerDay: durationCompleted
|
|
465
514
|
? Math.round((accepted * 86_400_000) / elapsedMs)
|
|
466
515
|
: null,
|
|
467
|
-
estimatedStoredEventsPerDay: durationCompleted &&
|
|
516
|
+
estimatedStoredEventsPerDay: durationCompleted && reconciliationPassed
|
|
468
517
|
? Math.round((storedEvents * 86_400_000) / elapsedMs)
|
|
469
518
|
: null,
|
|
470
519
|
certifiedEventsPerDay: certificationPassed
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
import type * as affiliates from "../affiliates.js";
|
|
12
12
|
import type * as constants from "../constants.js";
|
|
13
|
+
import type * as diagnostics from "../diagnostics.js";
|
|
13
14
|
import type * as errors from "../errors.js";
|
|
14
15
|
import type * as eventStore from "../eventStore.js";
|
|
15
16
|
import type * as funnels from "../funnels.js";
|
|
17
|
+
import type * as geo from "../geo.js";
|
|
16
18
|
import type * as goals from "../goals.js";
|
|
17
19
|
import type * as guards from "../guards.js";
|
|
18
20
|
import type * as http from "../http.js";
|
|
19
21
|
import type * as ingest from "../ingest.js";
|
|
20
22
|
import type * as live from "../live.js";
|
|
21
|
-
import type * as migrations from "../migrations.js";
|
|
22
23
|
import type * as reports from "../reports.js";
|
|
23
24
|
import type * as retention from "../retention.js";
|
|
24
25
|
import type * as sanitize from "../sanitize.js";
|
|
@@ -35,15 +36,16 @@ import { anyApi, componentsGeneric } from "convex/server";
|
|
|
35
36
|
const fullApi: ApiFromModules<{
|
|
36
37
|
affiliates: typeof affiliates;
|
|
37
38
|
constants: typeof constants;
|
|
39
|
+
diagnostics: typeof diagnostics;
|
|
38
40
|
errors: typeof errors;
|
|
39
41
|
eventStore: typeof eventStore;
|
|
40
42
|
funnels: typeof funnels;
|
|
43
|
+
geo: typeof geo;
|
|
41
44
|
goals: typeof goals;
|
|
42
45
|
guards: typeof guards;
|
|
43
46
|
http: typeof http;
|
|
44
47
|
ingest: typeof ingest;
|
|
45
48
|
live: typeof live;
|
|
46
|
-
migrations: typeof migrations;
|
|
47
49
|
reports: typeof reports;
|
|
48
50
|
retention: typeof retention;
|
|
49
51
|
sanitize: typeof sanitize;
|
|
@@ -77,6 +79,4 @@ export const internal: FilterApi<
|
|
|
77
79
|
FunctionReference<any, "internal">
|
|
78
80
|
> = anyApi as any;
|
|
79
81
|
|
|
80
|
-
export const components = componentsGeneric() as unknown as {
|
|
81
|
-
migrations: import("@convex-dev/migrations/_generated/component.js").ComponentApi<"migrations">;
|
|
82
|
-
};
|
|
82
|
+
export const components = componentsGeneric() as unknown as {};
|
|
@@ -65,6 +65,19 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
65
65
|
Name
|
|
66
66
|
>;
|
|
67
67
|
};
|
|
68
|
+
diagnostics: {
|
|
69
|
+
sessionEventTotals: FunctionReference<
|
|
70
|
+
"query",
|
|
71
|
+
"internal",
|
|
72
|
+
{ sessionIds: Array<string>; siteId: string },
|
|
73
|
+
{
|
|
74
|
+
foundSessions: number;
|
|
75
|
+
requestedSessions: number;
|
|
76
|
+
storedEvents: number;
|
|
77
|
+
},
|
|
78
|
+
Name
|
|
79
|
+
>;
|
|
80
|
+
};
|
|
68
81
|
funnels: {
|
|
69
82
|
list: FunctionReference<
|
|
70
83
|
"query",
|
|
@@ -39,6 +39,10 @@ import type { DataModel } from "./dataModel.js";
|
|
|
39
39
|
type Env = {
|
|
40
40
|
readonly CONVEX_CLOUD_URL: string;
|
|
41
41
|
readonly CONVEX_SITE_URL: string;
|
|
42
|
+
readonly RASTRO_GEOIP_DAILY_LIMIT: string | undefined;
|
|
43
|
+
readonly RASTRO_GEOIP_PROVIDER: "ipinfo" | "ipwhois" | undefined;
|
|
44
|
+
readonly RASTRO_GEOIP_TOKEN: string | undefined;
|
|
45
|
+
readonly RASTRO_TRUST_PROXY: "true" | undefined;
|
|
42
46
|
};
|
|
43
47
|
|
|
44
48
|
/**
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import migrations from "@convex-dev/migrations/convex.config.js";
|
|
2
1
|
import { defineComponent } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export default defineComponent("rastroAnalytics", {
|
|
5
|
+
env: {
|
|
6
|
+
RASTRO_GEOIP_PROVIDER: v.optional(
|
|
7
|
+
v.union(v.literal("ipinfo"), v.literal("ipwhois")),
|
|
8
|
+
),
|
|
9
|
+
RASTRO_GEOIP_TOKEN: v.optional(v.string()),
|
|
10
|
+
RASTRO_GEOIP_DAILY_LIMIT: v.optional(v.string()),
|
|
11
|
+
RASTRO_TRUST_PROXY: v.optional(v.literal("true")),
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { query } from "./_generated/server.js";
|
|
3
|
+
import { fail } from "./errors.js";
|
|
4
|
+
import { sanitizeOpaqueId } from "./sanitize.js";
|
|
5
|
+
|
|
6
|
+
const MAX_SESSION_TOTAL_IDS = 100;
|
|
7
|
+
|
|
8
|
+
export const sessionEventTotals = query({
|
|
9
|
+
args: {
|
|
10
|
+
siteId: v.id("sites"),
|
|
11
|
+
sessionIds: v.array(v.string()),
|
|
12
|
+
},
|
|
13
|
+
returns: v.object({
|
|
14
|
+
requestedSessions: v.number(),
|
|
15
|
+
foundSessions: v.number(),
|
|
16
|
+
storedEvents: v.number(),
|
|
17
|
+
}),
|
|
18
|
+
handler: async (ctx, args) => {
|
|
19
|
+
if (
|
|
20
|
+
args.sessionIds.length < 1 ||
|
|
21
|
+
args.sessionIds.length > MAX_SESSION_TOTAL_IDS
|
|
22
|
+
) {
|
|
23
|
+
fail(
|
|
24
|
+
"INVALID_ARGUMENT",
|
|
25
|
+
`sessionIds must contain between 1 and ${MAX_SESSION_TOTAL_IDS} IDs`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
let sessionIds: string[];
|
|
29
|
+
try {
|
|
30
|
+
sessionIds = args.sessionIds.map((sessionId) => {
|
|
31
|
+
const sanitized = sanitizeOpaqueId(sessionId, "sessionId");
|
|
32
|
+
if (sanitized !== sessionId) throw new Error("sessionId is not canonical");
|
|
33
|
+
return sanitized;
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
fail(
|
|
37
|
+
"INVALID_ARGUMENT",
|
|
38
|
+
error instanceof Error ? error.message : "invalid sessionId",
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (new Set(sessionIds).size !== sessionIds.length) {
|
|
42
|
+
fail("INVALID_ARGUMENT", "sessionIds must be unique");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sessions = await Promise.all(
|
|
46
|
+
sessionIds.map((sessionId) =>
|
|
47
|
+
ctx.db
|
|
48
|
+
.query("sessions")
|
|
49
|
+
.withIndex("by_siteId_and_sessionId", (range) =>
|
|
50
|
+
range.eq("siteId", args.siteId).eq("sessionId", sessionId),
|
|
51
|
+
)
|
|
52
|
+
.unique()
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
return {
|
|
56
|
+
requestedSessions: sessionIds.length,
|
|
57
|
+
foundSessions: sessions.filter((session) => session !== null).length,
|
|
58
|
+
storedEvents: sessions.reduce(
|
|
59
|
+
(total, session) => total + (session?.eventCount ?? 0),
|
|
60
|
+
0,
|
|
61
|
+
),
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|