@iann29/rastro 0.3.0 → 0.5.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 +199 -50
- package/agent/integration.md +26 -10
- package/agent/manifest.json +10 -5
- package/agent/manifest.schema.json +14 -5
- package/dist/client/federation.d.ts +28 -8
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +12 -1
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +262 -9
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +119 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +6 -0
- 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 +72 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +1 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/constants.d.ts +9 -2
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +31 -3
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.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 +6 -1
- package/dist/component/coverage.js.map +1 -1
- package/dist/component/eventStore.d.ts +2 -0
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/geo.d.ts +1 -0
- package/dist/component/geo.d.ts.map +1 -1
- package/dist/component/geo.js +1 -1
- package/dist/component/geo.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +65 -2
- package/dist/component/http.js.map +1 -1
- package/dist/component/identity.d.ts +12 -0
- package/dist/component/identity.d.ts.map +1 -1
- package/dist/component/identity.js +30 -2
- package/dist/component/identity.js.map +1 -1
- package/dist/component/ingest.d.ts +36 -0
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +111 -20
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +34 -4
- package/dist/component/live.js.map +1 -1
- package/dist/component/origin.d.ts +70 -0
- package/dist/component/origin.d.ts.map +1 -0
- package/dist/component/origin.js +230 -0
- package/dist/component/origin.js.map +1 -0
- package/dist/component/platforms.d.ts +33 -0
- package/dist/component/platforms.d.ts.map +1 -0
- package/dist/component/platforms.js +328 -0
- package/dist/component/platforms.js.map +1 -0
- package/dist/component/reports.d.ts +27 -72
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +72 -16
- package/dist/component/reports.js.map +1 -1
- package/dist/component/rollupStore.d.ts +7 -4
- package/dist/component/rollupStore.d.ts.map +1 -1
- package/dist/component/rollupStore.js +37 -17
- package/dist/component/rollupStore.js.map +1 -1
- package/dist/component/rollups.d.ts +27 -8
- package/dist/component/rollups.d.ts.map +1 -1
- package/dist/component/rollups.js +110 -32
- package/dist/component/rollups.js.map +1 -1
- package/dist/component/sanitize.d.ts +5 -0
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +33 -1
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +68 -9
- package/dist/component/schema.js +21 -3
- package/dist/component/schema.js.map +1 -1
- package/dist/component/trackedLinks.d.ts +91 -0
- package/dist/component/trackedLinks.d.ts.map +1 -0
- package/dist/component/trackedLinks.js +314 -0
- package/dist/component/trackedLinks.js.map +1 -0
- package/dist/component/useragent.d.ts +6 -0
- package/dist/component/useragent.d.ts.map +1 -1
- package/dist/component/useragent.js +9 -0
- package/dist/component/useragent.js.map +1 -1
- package/dist/component/validators.d.ts +51 -11
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +35 -1
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +11 -11
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +11 -11
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -0
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +104 -47
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker/vitals.d.ts.map +1 -1
- package/dist/tracker/vitals.js +22 -4
- package/dist/tracker/vitals.js.map +1 -1
- package/dist/tracker.min.js +1 -1
- package/dist/vitals.min.js +1 -1
- package/docs/upgrading.md +234 -45
- package/llms.txt +9 -4
- package/package.json +2 -2
- package/src/component/_generated/api.ts +6 -0
- package/src/component/_generated/component.ts +153 -1
- package/src/component/_generated/server.ts +1 -0
- package/src/component/constants.ts +31 -3
- package/src/component/convex.config.ts +3 -0
- package/src/component/coverage.ts +7 -1
- package/src/component/geo.ts +1 -1
- package/src/component/http.ts +71 -2
- package/src/component/identity.ts +41 -2
- package/src/component/ingest.ts +146 -33
- package/src/component/live.ts +51 -4
- package/src/component/origin.ts +273 -0
- package/src/component/platforms.ts +359 -0
- package/src/component/reports.ts +98 -18
- package/src/component/rollupStore.ts +51 -20
- package/src/component/rollups.ts +141 -32
- package/src/component/sanitize.ts +39 -1
- package/src/component/schema.ts +24 -3
- package/src/component/trackedLinks.ts +384 -0
- package/src/component/useragent.ts +11 -0
- package/src/component/validators.ts +71 -0
- package/src/tracker/generated.ts +11 -11
package/dist/tracker/vitals.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// The opt-in Web Vitals module.
|
|
3
|
-
//
|
|
2
|
+
// The opt-in Web Vitals module. Vitals annotate the session the base tracker
|
|
3
|
+
// opened — the server rejects them for a session it never saw — so this
|
|
4
|
+
// module derives its ids by exactly the tracker's rule, whichever runs first.
|
|
4
5
|
// Values are integers — milliseconds, except CLS which ships scaled by 1000.
|
|
5
6
|
(() => {
|
|
6
7
|
const script = document.currentScript;
|
|
@@ -10,15 +11,32 @@
|
|
|
10
11
|
if (!siteId || !endpoint)
|
|
11
12
|
return;
|
|
12
13
|
const createId = () => crypto.randomUUID?.() || Math.random() + "";
|
|
14
|
+
// The identity and session rule of tracker.ts: the host's signed-in id,
|
|
15
|
+
// then the persistent localStorage id, then the session id; a session is
|
|
16
|
+
// one identity's visit in one tab, closed by 30 minutes without an event.
|
|
17
|
+
let identity = script.dataset.visitor || "";
|
|
18
|
+
try {
|
|
19
|
+
identity ||= localStorage._rv ||= createId();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Blocked storage never breaks the page.
|
|
23
|
+
}
|
|
24
|
+
const now = Date.now();
|
|
13
25
|
let sessionId;
|
|
14
26
|
try {
|
|
15
|
-
sessionId =
|
|
27
|
+
sessionId =
|
|
28
|
+
(sessionStorage._ri === identity &&
|
|
29
|
+
now - sessionStorage._rt < 18e5 &&
|
|
30
|
+
sessionStorage._r) ||
|
|
31
|
+
createId();
|
|
16
32
|
sessionStorage._r = sessionId;
|
|
33
|
+
sessionStorage._ri = identity;
|
|
34
|
+
sessionStorage._rt = now;
|
|
17
35
|
}
|
|
18
36
|
catch {
|
|
19
37
|
sessionId = createId();
|
|
20
38
|
}
|
|
21
|
-
const visitorId =
|
|
39
|
+
const visitorId = identity || sessionId;
|
|
22
40
|
// Metrics describe the loaded document, so they keep the landing path even
|
|
23
41
|
// when a SPA rewrites the URL later.
|
|
24
42
|
const path = location.pathname.slice(0, 256);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitals.js","sourceRoot":"","sources":["../../src/tracker/vitals.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,6EAA6E;
|
|
1
|
+
{"version":3,"file":"vitals.js","sourceRoot":"","sources":["../../src/tracker/vitals.ts"],"names":[],"mappings":"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"}
|
package/dist/tracker.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{const
|
|
1
|
+
(()=>{const t=document.currentScript,e=t?.dataset.site,i=t?.dataset.endpoint||t?.src&&new URL("events",t.src).href;if(!e||!i)return;const n=()=>crypto.randomUUID?.()||Math.random()+"",s=new URLSearchParams(location.search),r=s.get("ref")?.slice(0,64);let o;for(const t of["source","medium","campaign","term","content"]){const e=s.get("utm_"+t)?.slice(0,64);e&&((o??={})["utm_"+t]=e)}const a="gclid gbraid wbraid msclkid ttclid twclid li_fat_id ScCid rdt_cid dclid epik srsltid mc_cid mc_eid _hsenc igsh igshid fbclid".split(" ").filter(t=>s.has(t)).slice(0,8);let c,d,l,h,p=t.dataset.visitor||"";try{p||=localStorage._rv||=n()}catch{}try{d=sessionStorage,l=d._r,c=d._ri==p&&d._rt,h=r||d._a,h&&(d._a=h),o||=d._u&&JSON.parse(d._u),o&&(d._u=JSON.stringify(o))}catch{h=r}let g,v=0;const m=[],u=document.referrer.split("/",3).join("/")||void 0,f=t=>{const n=m.splice(0,50);if(!n.length)return;const s=()=>new Blob([JSON.stringify({siteId:e,events:n,sentAt:Date.now()})]);let r=s();for(;r.size>64e3&&n.length>1;)m.unshift(n.pop()),r=s();t&&navigator.sendBeacon(i,r)||fetch(i,{method:"POST",body:r,keepalive:1}).catch(()=>{}),m.length&&f(t)},_=(t,e,i)=>{const s=Date.now(),r=s-c;c=s,r<18e5||(l=n(),"pageview"!=t&&(y="",S()));try{d._r=l,d._ri=p,d._rt=s}catch{}m.push({eventId:`${l}.${s.toString(36)}.${v}`,sessionId:l,visitorId:p||l,type:t,name:e,path:location.pathname.slice(0,256),referrer:u,timestamp:s,sequence:v++,affiliateSlug:h,...i}),clearTimeout(g),g=setTimeout(f,1200)};let y;const S=()=>{const t=location.pathname;t!=y&&(y=t,_("pageview",void 0,{properties:o,clid:a.length?a:void 0}))};window.rastro=(t,e,i)=>{if("context"==t)return{sessionId:l,visitorId:p||l};"event"==t&&_("custom",`${e||"event"}`.slice(0,80),i&&JSON.stringify(i).length>2e4?void 0:{properties:i})},document.addEventListener("click",t=>{const e=t.target?.closest("a,button,[data-rastro-event]");if(!e)return;const i=e.dataset.rastroEvent,n=e.href,s=n&&new URL(n),r=/^https?:$/.test(s?.protocol),o=r&&s.origin!=location.origin;_(i?"custom":o?"outbound":"click",i,{target:(e.dataset.rastroLabel||e.innerText||e.tagName).slice(0,64),href:r?((o?s.origin:"")+s.pathname).slice(0,256):void 0})},1);for(const t of["pushState","replaceState"]){const e=history[t];history[t]=(...t)=>{e.apply(history,t),S()}}addEventListener("popstate",S);let w=0;const b=()=>{_("heartbeat"),f()},I=()=>{w&&(clearInterval(w),w=0,_("leave"),f(1))},L=()=>{document.hidden?I():w||(b(),w=setInterval(b,2e4))};addEventListener("pagehide",I),addEventListener("pageshow",L),document.addEventListener("visibilitychange",L),w=setInterval(b,2e4),S(),L()})();
|
package/dist/vitals.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{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
|
|
1
|
+
(()=>{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 s=t.dataset.visitor||"";try{s||=localStorage._rv||=r()}catch{}const o=Date.now();let a;try{a=sessionStorage._ri===s&&o-sessionStorage._rt<18e5&&sessionStorage._r||r(),sessionStorage._r=a,sessionStorage._ri=s,sessionStorage._rt=o}catch{a=r()}const i=s||a,c=location.pathname.slice(0,256),d={},h=[],l=(t,e,n)=>{try{const r=new PerformanceObserver(t=>e(t.getEntries()));r.observe({type:t,buffered:1,...n}),h.push({observer:r,handler:e})}catch{}},f=performance.getEntriesByType?.("navigation")[0];f&&f.responseStart>0&&(d.TTFB=f.responseStart),l("paint",t=>{for(const e of t)"first-contentful-paint"===e.name&&(d.FCP=e.startTime)}),l("largest-contentful-paint",t=>{const e=t[t.length-1];e&&(d.LCP=e.startTime)});let m=0,u=0,v=0;l("layout-shift",t=>{for(const e of t)e.hadRecentInput||(m&&e.startTime-v<1e3&&e.startTime-u<5e3?m+=e.value:(m=e.value,u=e.startTime),v=e.startTime,d.CLS=Math.max(d.CLS||0,1e3*m))});const g=[],p=new Set,S=t=>{for(const e of t)e.interactionId&&(p.add(e.interactionId),g.push(e.duration));if(!g.length)return;g.sort((t,e)=>e-t),g.length=Math.min(g.length,10);const e=performance.interactionCount||p.size;d.INP=g[Math.min(g.length-1,Math.floor(e/50))]};l("event",S,{durationThreshold:40}),l("first-input",S);let y=0;const b=()=>{if(y)return;for(const{observer:t,handler:e}of h)e(t.takeRecords()),t.disconnect();const t=Date.now(),r=Object.keys(d).map((e,n)=>({eventId:`${a}.${t.toString(36)}.v${n}`,sessionId:a,visitorId:i,type:"vital",name:e,value:Math.round(d[e]),path:c,timestamp:t,sequence:n}));if(!r.length)return;y=1;const s=new Blob([JSON.stringify({siteId:e,events:r,sentAt:t})]),o=navigator.sendBeacon?.bind(navigator);o&&o(n,s)||fetch(n,{method:"POST",body:s,keepalive:1}).catch(()=>{})};document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&b()}),addEventListener("pagehide",b)})();
|
package/docs/upgrading.md
CHANGED
|
@@ -7,28 +7,240 @@ hosts still on them.
|
|
|
7
7
|
|
|
8
8
|
## Release matrix
|
|
9
9
|
|
|
10
|
-
| Current state | Required path
|
|
11
|
-
| ----------------------------------------------------- |
|
|
12
|
-
| Fresh deployment | Install the intended release directly
|
|
13
|
-
| `0.1.0-alpha.0` with maintained data | Install `0.1.0-alpha.1` first and wait for staged visitor-index backfills
|
|
14
|
-
| `0.1.0-alpha.1` | Wait for staged visitor-index backfills, then install `alpha.2`
|
|
15
|
-
| `0.1.0-alpha.2` with legacy `events` rows | Stay on `alpha.2` and run/resume migration
|
|
16
|
-
| `0.1.0-alpha.3` fresh/canonical deployment | Rehearse the next alpha normally
|
|
17
|
-
| `0.1.0-alpha.3` through `alpha.5` with canonical data | Install `0.1.0-alpha.6` directly; Web Vitals tables are additive
|
|
18
|
-
| `0.1.0-alpha.6` | Install `0.1.0-alpha.7` directly; no schema change
|
|
19
|
-
| `0.1.0-alpha.7` | Install `0.1.0-alpha.8` directly; site map tables are additive; re-export `siteMap`
|
|
20
|
-
| `0.1.0-alpha.8` | Install `0.1.0-alpha.10` directly; no schema change; the tracker changes
|
|
21
|
-
| `0.1.0-alpha.9` | Install `0.1.0-alpha.10` directly; `alpha.9` is deprecated — it rejected its own leave beacons
|
|
22
|
-
| `0.1.0-alpha.10` | Install `0.1.0-alpha.12` directly; new tables and fields are additive; re-export the configure functions to opt in
|
|
23
|
-
| `0.1.0-alpha.11` | Install `0.1.0-alpha.12` directly; same code, the package now ships `src/tracker/generated.ts`
|
|
24
|
-
| `0.1.0-alpha.12` | Install `0.1.0` directly; every schema change is an optional field or a new table; add `analytics:public` to a grant only to allow public links
|
|
25
|
-
| `0.1.0` | Install `0.2.0` directly; add the optional host callback and grant version to enable transfers
|
|
26
|
-
|
|
|
10
|
+
| Current state | Required path | Success condition |
|
|
11
|
+
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
|
12
|
+
| Fresh deployment | Install the intended release directly | Component deploys and `/rastro/health` succeeds |
|
|
13
|
+
| `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` |
|
|
14
|
+
| `0.1.0-alpha.1` | Wait for staged visitor-index backfills, then install `alpha.2` | Backfills complete before active readers deploy |
|
|
15
|
+
| `0.1.0-alpha.2` with legacy `events` rows | Stay on `alpha.2` and run/resume migration | Durable success plus empty source |
|
|
16
|
+
| `0.1.0-alpha.3` fresh/canonical deployment | Rehearse the next alpha normally | Schema, reports, tracker, and retention checks pass |
|
|
17
|
+
| `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"` |
|
|
18
|
+
| `0.1.0-alpha.6` | Install `0.1.0-alpha.7` directly; no schema change | `GET /rastro/tracker.js` contains `sentAt` |
|
|
19
|
+
| `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"` |
|
|
20
|
+
| `0.1.0-alpha.8` | Install `0.1.0-alpha.10` directly; no schema change; the tracker changes | `/rastro/health` reports `liveDepartures: "leaveBeacon"` |
|
|
21
|
+
| `0.1.0-alpha.9` | Install `0.1.0-alpha.10` directly; `alpha.9` is deprecated — it rejected its own leave beacons | The final page-close `POST /rastro/events` returns 200 |
|
|
22
|
+
| `0.1.0-alpha.10` | Install `0.1.0-alpha.12` directly; new tables and fields are additive; re-export the configure functions to opt in | `/rastro/health` reports `tracker.version: "0.1.0-alpha.12"` |
|
|
23
|
+
| `0.1.0-alpha.11` | Install `0.1.0-alpha.12` directly; same code, the package now ships `src/tracker/generated.ts` | A `convex/` test importing `@iann29/rastro/test` typechecks on push |
|
|
24
|
+
| `0.1.0-alpha.12` | Install `0.1.0` directly; every schema change is an optional field or a new table; add `analytics:public` to a grant only to allow public links | `/rastro/health` reports `tracker.version: "0.1.0"` |
|
|
25
|
+
| `0.1.0` | Install `0.2.0` directly; add the optional host callback and grant version to enable transfers | Health reports `0.2.0`; opted-in connectors advertise `transfer` |
|
|
26
|
+
| `0.2.0` | Install `0.3.0` directly; no new table; export `getSession` and `revenueSummary` from the federation module | Health reports `0.3.0`; the manifest lists `sessionDetails` |
|
|
27
|
+
| `0.3.0` | Install `0.4.0` directly; no re-export; the deploy rebuilds the `reportRollupQueue` index and the tracker changes | Health reports `0.4.0`; a hidden tab leaves **Ao vivo** in ~10 s |
|
|
28
|
+
| `0.4.0` | Install `0.5.0` directly; every schema change is an optional field or a new table; export `listTrackedLinks`, `createTrackedLink`, `updateTrackedLink` and `deleteTrackedLink` from the federation module; reverify — the manifest advertises `trackedLinks` | Health reports `0.5.0`; the manifest lists `trackedLinks` |
|
|
29
|
+
| Any release whose packed declarations lack federation | Stop and request an eligible exact registry release | Packed declarations contain the federation exports |
|
|
27
30
|
|
|
28
31
|
Do not upgrade a populated legacy deployment directly to a release that removes
|
|
29
32
|
the `events` table. Schema acceptance alone does not prove that telemetry was
|
|
30
33
|
migrated.
|
|
31
34
|
|
|
35
|
+
## Origins and tracked links (0.5.0)
|
|
36
|
+
|
|
37
|
+
Install `@iann29/rastro@0.5.0` and deploy the host through its normal workflow.
|
|
38
|
+
Every schema change is an optional field or a new table, so no staged phase and
|
|
39
|
+
no backfill are needed. Then add the four tracked-link functions to the
|
|
40
|
+
destructure in `convex/rastroFederation.ts`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export const {
|
|
44
|
+
transferConnection,
|
|
45
|
+
manifest,
|
|
46
|
+
connectionStatus,
|
|
47
|
+
listSites,
|
|
48
|
+
overview,
|
|
49
|
+
liveVisitors,
|
|
50
|
+
getSession,
|
|
51
|
+
revenueSummary,
|
|
52
|
+
listSessions,
|
|
53
|
+
sessionJourney,
|
|
54
|
+
listConversions,
|
|
55
|
+
visitorJourney,
|
|
56
|
+
goalsReport,
|
|
57
|
+
funnelsReport,
|
|
58
|
+
affiliatesReport,
|
|
59
|
+
vitalsReport,
|
|
60
|
+
siteMap,
|
|
61
|
+
dataCoverage,
|
|
62
|
+
siteSettings,
|
|
63
|
+
updateSite,
|
|
64
|
+
listGoals,
|
|
65
|
+
upsertGoal,
|
|
66
|
+
removeGoal,
|
|
67
|
+
listFunnels,
|
|
68
|
+
upsertFunnel,
|
|
69
|
+
removeFunnel,
|
|
70
|
+
listAffiliates,
|
|
71
|
+
upsertAffiliate,
|
|
72
|
+
removeAffiliate,
|
|
73
|
+
retentionStatus,
|
|
74
|
+
setRetentionPolicy,
|
|
75
|
+
disableRetentionPolicy,
|
|
76
|
+
listTrackedLinks,
|
|
77
|
+
createTrackedLink,
|
|
78
|
+
updateTrackedLink,
|
|
79
|
+
deleteTrackedLink,
|
|
80
|
+
} = federated;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Deploy again, then open **Conexões** and click **Verificar novamente** so the
|
|
84
|
+
control plane records the `trackedLinks` capability. Listing links needs only a
|
|
85
|
+
reader; creating, changing and deleting them need `analytics:configure` in both
|
|
86
|
+
the token and the local grant, like goals and affiliates. A host that skips the
|
|
87
|
+
re-export keeps working: the connector simply does not advertise the capability,
|
|
88
|
+
and the dashboard's **Links** tab says what is missing.
|
|
89
|
+
|
|
90
|
+
What reports show differently:
|
|
91
|
+
|
|
92
|
+
- **Fontes in three levels.** Visão geral's flat "Origens" list becomes
|
|
93
|
+
**Fontes**: the channel a session arrived by, the platform inside it, and the
|
|
94
|
+
raw hosts inside that. A host outside the classifier's table lands under
|
|
95
|
+
Referência › Outros.
|
|
96
|
+
- **Sessions from before the upgrade** are classified on read from what they
|
|
97
|
+
kept, marked `evidence: "legado"` (`legacy` in the API), and never enter
|
|
98
|
+
`topPlatforms`, `topChannels` or `topEvidence`. Nothing is backfilled.
|
|
99
|
+
- **Classificado desde.** `dataCoverage`'s new `origin` dataset says from when a
|
|
100
|
+
site carries origins — its first ingest on this release plus the rollup delay
|
|
101
|
+
— and the caption reads "Classificado desde"; it says nothing rather than
|
|
102
|
+
guessing when it cannot read the date.
|
|
103
|
+
- **One rule decides.** A declared `utm_source`, then a click-ID name, then the
|
|
104
|
+
referrer host, then the in-app browser, then an affiliate `ref`. So a landing
|
|
105
|
+
with `fbclid` alone is Meta by click ID, and nothing at all — the only way to
|
|
106
|
+
be `direct`. WhatsApp, Telegram, SMS and native e-mail apps stay `direct`
|
|
107
|
+
unless a tracked link or a UTM proves them.
|
|
108
|
+
- **Attribution quality.** A strip above the tree reads `topEvidence`: how much
|
|
109
|
+
of the traffic a UTM, a click ID, a referrer, an in-app browser or an
|
|
110
|
+
affiliate proved, and how much proves nothing.
|
|
111
|
+
- **`topPlatforms` values** are the platform id alone when the session arrived
|
|
112
|
+
by that platform's default channel and `platform@channel` when it did not
|
|
113
|
+
(`google@paid_search`); `splitPlatformDimension` reads the pair back.
|
|
114
|
+
`source`, `topSources`, `topCampaigns` and `topMediums` are unchanged.
|
|
115
|
+
|
|
116
|
+
Tracked links:
|
|
117
|
+
|
|
118
|
+
- `GET /rastro/l/<slug>` answers `302` with `Cache-Control: no-store` to the
|
|
119
|
+
link's destination, appending `utm_source` (the platform), `utm_medium` (the
|
|
120
|
+
channel), `utm_campaign` (the campaign, or the slug when the link has none)
|
|
121
|
+
and `utm_content` (the slug). The destination's own query stays, and a `utm_*`
|
|
122
|
+
key it already carries is never overridden. An unknown or disabled slug
|
|
123
|
+
answers `404` in plain text.
|
|
124
|
+
- **No open redirect.** A destination must be an absolute `http(s)` URL without
|
|
125
|
+
credentials on one of the site's domains (exact, or a `*.` pattern). The rule
|
|
126
|
+
is checked when the link is created or changed and again on every click, so a
|
|
127
|
+
domain removed from the site stops redirecting at once.
|
|
128
|
+
- **Bots.** A self-declared bot, the WhatsApp, Facebook and Telegram link
|
|
129
|
+
previews included, is still redirected but counted under `bots`, never
|
|
130
|
+
`clicks`. Clicks land in daily counters spread over four shards.
|
|
131
|
+
- **Slugs** are 3–32 lowercase letters, digits or hyphens and unique across the
|
|
132
|
+
deployment, because the route names no site. A site keeps at most 200 links.
|
|
133
|
+
- **A short domain.** The route lives on the deployment's HTTP origin
|
|
134
|
+
(`https://<deployment>.convex.site/rastro/l/promo`). A host that wants a
|
|
135
|
+
presentable link serves that origin under a short domain of its own, such as
|
|
136
|
+
`go.example.com/rastro/l/promo`; nothing in the component changes.
|
|
137
|
+
|
|
138
|
+
Tracker:
|
|
139
|
+
|
|
140
|
+
- It sends the click-ID key **names** of the landing URL in `clid` (`fbclid`,
|
|
141
|
+
`gclid`, `ttclid`, `mc_cid`…), never their values, at most eight; ingestion
|
|
142
|
+
drops any name outside the allowlist. No advertising identifier leaves the
|
|
143
|
+
page.
|
|
144
|
+
- The referrer is sent as scheme and host, so a referrer of the form
|
|
145
|
+
`android-app://<package>` now reaches the server with its package instead of
|
|
146
|
+
the literal `"null"`: an Instagram or Gmail tap on Android classifies as that
|
|
147
|
+
platform.
|
|
148
|
+
- The script is 2,594 bytes raw and 1,395 bytes gzip, and its budget moved from
|
|
149
|
+
1,280 to 1,400 bytes gzip. Hosts serving it from the component get it on
|
|
150
|
+
upgrade, with a new `tracker.hash` and `ETag`; returning browsers pick it up
|
|
151
|
+
within the cache window (see [Tracker cache](#tracker-cache)).
|
|
152
|
+
|
|
153
|
+
Acceptance for this upgrade:
|
|
154
|
+
|
|
155
|
+
- `/rastro/health` reports `tracker.version: "0.5.0"` and a new `tracker.hash`.
|
|
156
|
+
- A `GET /rastro/l/<slug>` on a link created from the dashboard answers `302`
|
|
157
|
+
with `utm_source`, `utm_medium`, `utm_campaign` and `utm_content` on the
|
|
158
|
+
destination.
|
|
159
|
+
- **Conexões → Verificar novamente** shows the `trackedLinks` capability and the
|
|
160
|
+
**Links** tab appears.
|
|
161
|
+
- In a browser context opened after the deploy, a landing with `?gclid=x` shows
|
|
162
|
+
in **Fontes** under Busca paga › Google.
|
|
163
|
+
|
|
164
|
+
## Sessions, presence and rollups (0.4.0)
|
|
165
|
+
|
|
166
|
+
`0.4.0` needs no host edit: no new function, capability, or re-export. Install
|
|
167
|
+
`@iann29/rastro@0.4.0`, deploy the host through its normal workflow, then open
|
|
168
|
+
**Conexões** and click **Verificar novamente** on the active connection.
|
|
169
|
+
|
|
170
|
+
What reports show differently:
|
|
171
|
+
|
|
172
|
+
- **Sessions.** A session is one identity's visit in one tab: a sign-in (a new
|
|
173
|
+
`data-visitor`) or 30 minutes without an event opens a new one, keeping the
|
|
174
|
+
referral and campaign. A tab returning after 30 idle minutes counts a pageview
|
|
175
|
+
of its current page. A tab open during the upgrade starts one new session on
|
|
176
|
+
its next page load.
|
|
177
|
+
- **Presence.** A hidden tab sends `leave` and drops out of **Ao vivo** about
|
|
178
|
+
ten seconds later; shown again, it sends a heartbeat at once and returns. A
|
|
179
|
+
hidden tab sends no requests.
|
|
180
|
+
- **Web Vitals** need the base tracker on the page; `vitals.js` now derives its
|
|
181
|
+
ids by the tracker's rule, so the default install's vitals are accepted.
|
|
182
|
+
- **Duration** is counted at departure. Heartbeats no longer rewrite aggregate
|
|
183
|
+
buckets, so a visitor still on a page shows the time up to their last page or
|
|
184
|
+
event; the rest lands when they leave (about ten seconds after a `leave`, at
|
|
185
|
+
most 90 seconds after the last heartbeat). Finished visits are unchanged.
|
|
186
|
+
- **Rollups** fold about once a second per site, so bucketed reports trail
|
|
187
|
+
ingestion by about a second.
|
|
188
|
+
- **Overview.** A busy range fails with `REPORT_INCOMPLETE` instead of a raw
|
|
189
|
+
read-limit error, and `LIMIT_EXCEEDED` names the real cap: 101 days for one
|
|
190
|
+
site, 10 for ten; hourly, 24 hours for two sites, 5 for ten.
|
|
191
|
+
- **Funnels.** A completed funnel can be entered again once its conversion
|
|
192
|
+
window has passed.
|
|
193
|
+
|
|
194
|
+
Operations:
|
|
195
|
+
|
|
196
|
+
- The deploy swaps the `reportRollupQueue` index for
|
|
197
|
+
`by_siteId_and_deadLetteredAt_and_createdAt` and backfills it during the push.
|
|
198
|
+
The queue holds only batches waiting for their fold, so no staged phase is
|
|
199
|
+
needed. A batch that fails three fold runs is logged and set aside for 30
|
|
200
|
+
days.
|
|
201
|
+
- Ingestion admits at most 1,200 events and 2 MiB a minute per site from one
|
|
202
|
+
client network (an IPv4 address or an IPv6 /64) when `/rastro/health` reports
|
|
203
|
+
`clientIpAvailable: true`; visitors behind one NAT share it. The per-site
|
|
204
|
+
budget drops to 384,000 events a minute. Over budget answers 429
|
|
205
|
+
`RATE_LIMITED`.
|
|
206
|
+
- `RASTRO_CLIENT_RATE_LIMIT=off` lifts the per-client budget for load-test
|
|
207
|
+
canaries only; leave it unset everywhere else.
|
|
208
|
+
|
|
209
|
+
Acceptance for this upgrade: `/rastro/health` reports `tracker.version: "0.4.0"`
|
|
210
|
+
and a new `tracker.hash`; `GET /rastro/tracker.js` answers with a new `ETag`,
|
|
211
|
+
which returning browsers pick up within the cache window (see
|
|
212
|
+
[Tracker cache](#tracker-cache)); in a browser context opened after the deploy,
|
|
213
|
+
a hidden tab disappears from **Ao vivo** within about ten seconds and returns
|
|
214
|
+
when shown again.
|
|
215
|
+
|
|
216
|
+
## Session details and complete revenue lists (0.3.0)
|
|
217
|
+
|
|
218
|
+
After upgrading the package, export `getSession` and `revenueSummary` from the
|
|
219
|
+
object returned by `exposeFederatedAnalyticsApi` in
|
|
220
|
+
`convex/rastroFederation.ts`, then deploy the host and reverify its dashboard
|
|
221
|
+
connection. The manifest advertises `sessionDetails` and `revenueSummary`;
|
|
222
|
+
advertising them without exporting the functions leaves an incomplete host
|
|
223
|
+
surface.
|
|
224
|
+
|
|
225
|
+
`getSession({siteId, sessionId})` returns the retained session metadata (or
|
|
226
|
+
null). The dashboard pairs it with the existing paginated `sessionJourney` so a
|
|
227
|
+
copied session link keeps its duration, entry/exit and counters even after a
|
|
228
|
+
reload. Older hosts still provide scoped session events, but cannot restore all
|
|
229
|
+
metadata.
|
|
230
|
+
|
|
231
|
+
`revenueSummary({siteIds, from, to})` returns exact `commissionCents` for the
|
|
232
|
+
inclusive period. Revenue, conversion count, average order and currency reuse
|
|
233
|
+
`overview` totals. The commission report reads existing daily affiliate stats
|
|
234
|
+
for complete UTC days and trusted conversion records only for partial edge days;
|
|
235
|
+
renamed/deleted affiliate definitions do not remove historical commissions. It
|
|
236
|
+
rejects unavailable retained sources, mixed currencies and periods requiring
|
|
237
|
+
more than 5,000 source rows instead of presenting partial totals. No new table,
|
|
238
|
+
backfill or ingestion change is required. Older hosts show commissions as
|
|
239
|
+
unavailable, never as the sum of a loaded list page.
|
|
240
|
+
|
|
241
|
+
Session and conversion lists now expose per-site keyset continuation, merge rows
|
|
242
|
+
in chronological order and label search as filtering only loaded records.
|
|
243
|
+
|
|
32
244
|
## Organization transfers (0.2.0)
|
|
33
245
|
|
|
34
246
|
Install `@iann29/rastro@0.2.0` after rehearsing on a non-production snapshot.
|
|
@@ -399,6 +611,11 @@ control-plane issuer.
|
|
|
399
611
|
functions, a read-only grant refuses `upsertGoal` with
|
|
400
612
|
`FEDERATION_CONFIGURE_FORBIDDEN`, and a grant listing `analytics:configure`
|
|
401
613
|
lets an owner or admin create a goal from the dashboard.
|
|
614
|
+
- On `0.4.0` or later: hiding the site's tab removes the visitor from the live
|
|
615
|
+
view within about ten seconds, and showing it brings them back.
|
|
616
|
+
- On `0.5.0` or later: the federation module re-exports the four tracked-link
|
|
617
|
+
functions, a created link redirects with its `utm_*`, and a landing with a
|
|
618
|
+
click ID appears under its channel and platform in **Fontes**.
|
|
402
619
|
- Protected overview, journey, goal, funnel, and affiliate reports used by the
|
|
403
620
|
host still run.
|
|
404
621
|
- Trusted conversion idempotency and currency checks still pass.
|
|
@@ -407,31 +624,3 @@ control-plane issuer.
|
|
|
407
624
|
|
|
408
625
|
Record the package version, deployment target, migration output, and verifier in
|
|
409
626
|
the project's release evidence.
|
|
410
|
-
|
|
411
|
-
### Session details and complete revenue lists
|
|
412
|
-
|
|
413
|
-
After upgrading the package, export `getSession` and `revenueSummary` from the
|
|
414
|
-
object returned by `exposeFederatedAnalyticsApi` in
|
|
415
|
-
`convex/rastroFederation.ts`, then deploy the host and reverify its dashboard
|
|
416
|
-
connection. The manifest advertises `sessionDetails` and `revenueSummary`;
|
|
417
|
-
advertising them without exporting the functions leaves an incomplete host
|
|
418
|
-
surface.
|
|
419
|
-
|
|
420
|
-
`getSession({siteId, sessionId})` returns the retained session metadata (or
|
|
421
|
-
null). The dashboard pairs it with the existing paginated `sessionJourney` so a
|
|
422
|
-
copied session link keeps its duration, entry/exit and counters even after a
|
|
423
|
-
reload. Older hosts still provide scoped session events, but cannot restore all
|
|
424
|
-
metadata.
|
|
425
|
-
|
|
426
|
-
`revenueSummary({siteIds, from, to})` returns exact `commissionCents` for the
|
|
427
|
-
inclusive period. Revenue, conversion count, average order and currency reuse
|
|
428
|
-
`overview` totals. The commission report reads existing daily affiliate stats
|
|
429
|
-
for complete UTC days and trusted conversion records only for partial edge days;
|
|
430
|
-
renamed/deleted affiliate definitions do not remove historical commissions. It
|
|
431
|
-
rejects unavailable retained sources, mixed currencies and periods requiring
|
|
432
|
-
more than 5,000 source rows instead of presenting partial totals. No new table,
|
|
433
|
-
backfill or ingestion change is required. Older hosts show commissions as
|
|
434
|
-
unavailable, never as the sum of a loaded list page.
|
|
435
|
-
|
|
436
|
-
Session and conversion lists now expose per-site keyset continuation, merge rows
|
|
437
|
-
in chronological order and label search as filtering only loaded records.
|
package/llms.txt
CHANGED
|
@@ -16,7 +16,9 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
16
16
|
## Architecture
|
|
17
17
|
|
|
18
18
|
- Tracker and telemetry: browser -> customer HTTP-actions URL -> customer Rastro component.
|
|
19
|
-
-
|
|
19
|
+
- The tracker is 2,594 bytes raw and 1,395 gzip. It sends the click-ID key **names** of the landing URL in `clid` (`gclid`, `fbclid`, `ttclid`…), at most eight, never a value, and the referrer as scheme and host, so an `android-app://<package>` referrer keeps its package.
|
|
20
|
+
- Tracked links (0.5.0+): browser -> customer HTTP-actions URL `/rastro/l/<slug>` -> `302` (`Cache-Control: no-store`) to a destination on one of the site's domains, with `utm_source` (platform), `utm_medium` (channel), `utm_campaign` (campaign, or the slug) and `utm_content` (slug) appended; the destination's own query and `utm_*` keys stay. Unknown or disabled slug: `404`. Clicks count in four-shard daily counters; self-declared bots are redirected and counted apart.
|
|
21
|
+
- Optional field Web Vitals (alpha.6+): a second browser module at `/rastro/vitals.js` reports LCP, CLS, INP, FCP, and TTFB to the same events endpoint, into bounded daily histograms; it needs the base tracker on the page.
|
|
20
22
|
- Reactive dashboard reads: amagerastro.com browser -> customer functions URL -> `rastroFederation:*`.
|
|
21
23
|
- Control plane: account, organization, connection metadata, short-lived token issuance, and revocation.
|
|
22
24
|
- The control plane does not proxy or persist customer analytics events.
|
|
@@ -30,7 +32,8 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
30
32
|
- Resolve an indexed, authoritative local grant on every federated query.
|
|
31
33
|
- Grant one through ten explicit site IDs. Never grant by owner-wide enumeration.
|
|
32
34
|
- Use the canonical module name `convex/rastroFederation.ts`.
|
|
33
|
-
-
|
|
35
|
+
- A host on 0.5.0 re-exports `listTrackedLinks`, `createTrackedLink`, `updateTrackedLink` and `deleteTrackedLink` from that module and reverifies, which advertises the optional `trackedLinks` capability; listing needs a reader, the other three need `analytics:configure`. A host that skips it keeps working without the capability.
|
|
36
|
+
- Expose only the manifest, connection status, site summaries, reports, journeys, conversions, affiliates, funnels, goals, web vitals, coverage, tracked links, and the configure functions listed in the manifest; the configure functions run only when the token and the local grant both carry `analytics:configure`. A public link's token carries `analytics:public` alone and reaches only the manifest, connection status, site summaries, a redacted overview, redacted live visitors, and coverage, and only when the local grant lists `analytics:public`.
|
|
34
37
|
- Use the functions URL as JWT audience and the HTTP-actions URL for tracker ingestion.
|
|
35
38
|
- Rehearse on an explicit development target. Production changes require fresh operator consent.
|
|
36
39
|
- Validate manifest, authenticated connection status, redacted site list, real ingestion, reactive live data, and local-first revocation.
|
|
@@ -49,19 +52,21 @@ Do not infer federation availability from this website, a dist-tag, or a source
|
|
|
49
52
|
- Protocol: `amage-rastro-analytics` version 1.
|
|
50
53
|
- Maximum sites per connection and report request: 10.
|
|
51
54
|
- Maximum report range: 366 days.
|
|
52
|
-
-
|
|
55
|
+
- Overview maximum, bounded by the aggregate rows it reads: 101 days for one site and 10 for ten; hourly, 24 hours for two sites and 5 for ten. A wider range fails with `LIMIT_EXCEEDED`, a busy one with `REPORT_INCOMPLETE`.
|
|
53
56
|
- Funnel report maximum: 90 complete UTC days.
|
|
54
57
|
- Affiliate reports require complete UTC-day boundaries.
|
|
55
58
|
- Vitals report maximum: 90 complete UTC days; `vitals` is an optional capability that hosts before `alpha.6` do not advertise.
|
|
56
59
|
- 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
60
|
- Maximum live visitors: 500.
|
|
58
|
-
-
|
|
61
|
+
- Tracked links: at most 200 per site; slugs are 3–32 lowercase letters, digits or hyphens, unique across the deployment; `listTrackedLinks` reports clicks and bots all time and over the 30 UTC days ending on the `now` it is given. `trackedLinks` is an optional capability that hosts before 0.5.0, and hosts that did not re-export the four functions, do not advertise.
|
|
62
|
+
- Live window: the tracker sends a `leave` when the tab hides or the page unloads, retiring the visitor about ten seconds later; a shown tab or a bfcache return sends a heartbeat at once and 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
63
|
- Maximum journey events: 500.
|
|
60
64
|
- Token lifetime: 10 minutes, RS256. Every member's token carries `analytics:read`; owners and admins also carry `analytics:configure`, which the host honors only when the local grant lists it (`configure` is an optional capability that hosts before `alpha.11` do not advertise). A public link's token carries `analytics:public` alone, plus `rastro_site_ids` naming the link's sites; hosts before `0.1.0` reject it.
|
|
61
65
|
|
|
62
66
|
## Read semantics
|
|
63
67
|
|
|
64
68
|
- Times are inclusive Unix epoch milliseconds in UTC.
|
|
69
|
+
- Origin (0.5.0+): a session is classified once, on its first touch, into `platform` (about forty ids), `channel` (twelve, `direct` to `ai` plus `offline`) and `evidence`, the rule that decided it. Decision order, first match wins: declared `utm_source`, click-ID name, referrer host by whole label, in-app browser, affiliate `ref`; nothing at all is the only way to be `direct`. `overview.topPlatforms`, `topChannels` and `topEvidence` rank them beside `topSources`; a `topPlatforms` value is the bare platform id for its default channel and `platform@channel` otherwise. `dataCoverage`'s `origin` dataset says from when a site carries origins; older sessions are classified on read, marked `evidence: "legacy"`, and never enter the aggregates. `source` is unchanged.
|
|
65
70
|
- Goal, funnel, and affiliate reports include read-only definitions plus aggregates. Configuration writes (goals, funnels, affiliates, a site's name/domains/timezone, retention) exist only in the configure scope and fail with `FEDERATION_CONFIGURE_FORBIDDEN` for a read-only token or grant; `listSites` never returns domains, only `siteSettings` does, to a connection allowed to change them.
|
|
66
71
|
- A public token (`analytics:public`) gets `overview` with zero `conversions` and `revenueCents` and empty `topCampaigns`, `topMediums`, `topEvents`, and `topGoals`, `liveVisitors` with `visitorId` equal to `sessionId` and without `entryPath`, `returning`, `utmCampaign`, `affiliateSlug`, `conversionCount`, or `funnel`, and `FEDERATION_PUBLIC_FORBIDDEN` for sessions, journeys, conversions, goals, funnels, affiliates, vitals, the site map, and the configure scope.
|
|
67
72
|
- Session and visitor journeys include bounded custom properties. Hosts must never place personal data or secrets in event properties.
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iann29/rastro",
|
|
3
|
-
"description": "Privacy-first, real-time web analytics for Convex: live visitor atlas, journeys, revenue, goals, funnels, affiliates, and a 1.
|
|
3
|
+
"description": "Privacy-first, real-time web analytics for Convex: live visitor atlas, journeys, revenue, goals, funnels, affiliates, and a 1.4 KB tracker.",
|
|
4
4
|
"repository": "github:amageweb/amage-rastro",
|
|
5
5
|
"homepage": "https://github.com/amageweb/amage-rastro#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/amageweb/amage-rastro/issues"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.5.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -24,12 +24,15 @@ import type * as identity from "../identity.js";
|
|
|
24
24
|
import type * as ingest from "../ingest.js";
|
|
25
25
|
import type * as live from "../live.js";
|
|
26
26
|
import type * as localTime from "../localTime.js";
|
|
27
|
+
import type * as origin from "../origin.js";
|
|
28
|
+
import type * as platforms from "../platforms.js";
|
|
27
29
|
import type * as reports from "../reports.js";
|
|
28
30
|
import type * as retention from "../retention.js";
|
|
29
31
|
import type * as rollupStore from "../rollupStore.js";
|
|
30
32
|
import type * as rollups from "../rollups.js";
|
|
31
33
|
import type * as sanitize from "../sanitize.js";
|
|
32
34
|
import type * as sites from "../sites.js";
|
|
35
|
+
import type * as trackedLinks from "../trackedLinks.js";
|
|
33
36
|
import type * as useragent from "../useragent.js";
|
|
34
37
|
import type * as validators from "../validators.js";
|
|
35
38
|
import type * as visitors from "../visitors.js";
|
|
@@ -59,12 +62,15 @@ const fullApi: ApiFromModules<{
|
|
|
59
62
|
ingest: typeof ingest;
|
|
60
63
|
live: typeof live;
|
|
61
64
|
localTime: typeof localTime;
|
|
65
|
+
origin: typeof origin;
|
|
66
|
+
platforms: typeof platforms;
|
|
62
67
|
reports: typeof reports;
|
|
63
68
|
retention: typeof retention;
|
|
64
69
|
rollupStore: typeof rollupStore;
|
|
65
70
|
rollups: typeof rollups;
|
|
66
71
|
sanitize: typeof sanitize;
|
|
67
72
|
sites: typeof sites;
|
|
73
|
+
trackedLinks: typeof trackedLinks;
|
|
68
74
|
useragent: typeof useragent;
|
|
69
75
|
validators: typeof validators;
|
|
70
76
|
visitors: typeof visitors;
|