@iann29/rastro 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/agent/integration.md +4 -0
- package/agent/manifest.json +8 -4
- package/agent/manifest.schema.json +7 -1
- package/dist/client/federation.d.ts +18 -8
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +7 -1
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +138 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +30 -0
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +45 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +19 -6
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/reports.d.ts +47 -0
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +75 -0
- package/dist/component/reports.js.map +1 -1
- package/dist/tracker/generated.d.ts +1 -1
- package/dist/tracker/generated.js +1 -1
- package/docs/federation-setup.md +2 -0
- package/docs/federation.md +6 -0
- package/docs/upgrading.md +28 -0
- package/package.json +1 -1
- package/src/component/_generated/component.ts +48 -0
- package/src/component/ingest.ts +30 -6
- package/src/component/reports.ts +96 -0
- package/src/tracker/generated.ts +1 -1
package/src/component/reports.ts
CHANGED
|
@@ -932,6 +932,102 @@ async function readRouteRollups(
|
|
|
932
932
|
return { rows, completeFrom: cutAt + DAY_MS };
|
|
933
933
|
}
|
|
934
934
|
|
|
935
|
+
/** Metadata for a selected session, independent of list pagination. */
|
|
936
|
+
export const getSession = query({
|
|
937
|
+
args: { siteId: v.id("sites"), sessionId: v.string() },
|
|
938
|
+
returns: v.union(sessionDocumentValidator, v.null()),
|
|
939
|
+
handler: async (ctx, args) => {
|
|
940
|
+
const session = await ctx.db
|
|
941
|
+
.query("sessions")
|
|
942
|
+
.withIndex("by_siteId_and_sessionId", (q) =>
|
|
943
|
+
q.eq("siteId", args.siteId).eq("sessionId", args.sessionId),
|
|
944
|
+
)
|
|
945
|
+
.unique();
|
|
946
|
+
if (!session) return null;
|
|
947
|
+
const {
|
|
948
|
+
geoLookupAttemptedAt: _geo,
|
|
949
|
+
lastPageviewAt: _pageview,
|
|
950
|
+
...result
|
|
951
|
+
} = session;
|
|
952
|
+
return result;
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
/** Exact commissions alongside overview's period revenue/conversion totals. */
|
|
957
|
+
export const revenueSummary = query({
|
|
958
|
+
args: { siteIds: v.array(v.id("sites")), from: v.number(), to: v.number() },
|
|
959
|
+
returns: v.object({ commissionCents: v.number() }),
|
|
960
|
+
handler: async (ctx, args) => {
|
|
961
|
+
const siteIds = validateSiteIds(args.siteIds);
|
|
962
|
+
validateRange(args.from, args.to, MAX_REPORT_RANGE_DAYS);
|
|
963
|
+
const fullFrom = Math.ceil(args.from / DAY_MS) * DAY_MS;
|
|
964
|
+
const fullTo = Math.floor((args.to + 1) / DAY_MS) * DAY_MS;
|
|
965
|
+
let commissionCents = 0;
|
|
966
|
+
let rowsRead = 0;
|
|
967
|
+
const currencies = new Set<string>();
|
|
968
|
+
for (const siteId of siteIds) {
|
|
969
|
+
const site = await ctx.db.get("sites", siteId);
|
|
970
|
+
if (!site) fail("NOT_FOUND", "site not found");
|
|
971
|
+
currencies.add(site.currency ?? "USD");
|
|
972
|
+
if (currencies.size > 1)
|
|
973
|
+
fail("CURRENCY_MISMATCH", "revenue sites must use the same currency");
|
|
974
|
+
if (fullFrom < fullTo) {
|
|
975
|
+
await assertSourceAvailable(
|
|
976
|
+
ctx,
|
|
977
|
+
[siteId],
|
|
978
|
+
["affiliateStats"],
|
|
979
|
+
fullFrom,
|
|
980
|
+
);
|
|
981
|
+
const stats = await ctx.db
|
|
982
|
+
.query("affiliateDailyStats")
|
|
983
|
+
.withIndex("by_siteId_and_bucketStart", (q) =>
|
|
984
|
+
q
|
|
985
|
+
.eq("siteId", siteId)
|
|
986
|
+
.gte("bucketStart", fullFrom)
|
|
987
|
+
.lt("bucketStart", fullTo),
|
|
988
|
+
)
|
|
989
|
+
.take(MAX_REPORT_BUCKETS - rowsRead + 1);
|
|
990
|
+
rowsRead += stats.length;
|
|
991
|
+
failIfIncomplete(
|
|
992
|
+
stats,
|
|
993
|
+
MAX_REPORT_BUCKETS - (rowsRead - stats.length),
|
|
994
|
+
"commission buckets",
|
|
995
|
+
);
|
|
996
|
+
for (const row of stats) commissionCents += row.commissionCents;
|
|
997
|
+
}
|
|
998
|
+
// ponytail: boundary scans cap at MAX_REPORT_BUCKETS; add hourly commission
|
|
999
|
+
// rollups if busy partial days exceed this explicit report limit.
|
|
1000
|
+
// Daily stats cannot answer partial days (24h and local-day windows).
|
|
1001
|
+
// Read only those edges from the trusted ledger, never round the period.
|
|
1002
|
+
const edges: Array<[number, number]> =
|
|
1003
|
+
fullFrom < fullTo
|
|
1004
|
+
? [
|
|
1005
|
+
[args.from, fullFrom - 1],
|
|
1006
|
+
[fullTo, args.to],
|
|
1007
|
+
]
|
|
1008
|
+
: [[args.from, args.to]];
|
|
1009
|
+
for (const [from, to] of edges) {
|
|
1010
|
+
if (from > to) continue;
|
|
1011
|
+
await assertSourceAvailable(ctx, [siteId], ["conversions"], from);
|
|
1012
|
+
const rows = await ctx.db
|
|
1013
|
+
.query("conversions")
|
|
1014
|
+
.withIndex("by_siteId_and_timestamp", (q) =>
|
|
1015
|
+
q.eq("siteId", siteId).gte("timestamp", from).lte("timestamp", to),
|
|
1016
|
+
)
|
|
1017
|
+
.take(MAX_REPORT_BUCKETS - rowsRead + 1);
|
|
1018
|
+
rowsRead += rows.length;
|
|
1019
|
+
failIfIncomplete(
|
|
1020
|
+
rows,
|
|
1021
|
+
MAX_REPORT_BUCKETS - (rowsRead - rows.length),
|
|
1022
|
+
"commission boundary conversions",
|
|
1023
|
+
);
|
|
1024
|
+
for (const row of rows) commissionCents += row.commissionCents;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return { commissionCents };
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
|
|
935
1031
|
export const listSessions = query({
|
|
936
1032
|
args: {
|
|
937
1033
|
siteId: v.id("sites"),
|
package/src/tracker/generated.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/build-tracker.mjs. Do not edit.
|
|
2
|
-
export const RASTRO_VERSION = "0.
|
|
2
|
+
export const RASTRO_VERSION = "0.3.0";
|
|
3
3
|
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()})();";
|
|
4
4
|
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==";
|
|
5
5
|
export const TRACKER_RAW_BYTES = 2271;
|