@iann29/rastro 0.6.0 → 0.7.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 +27 -0
- package/agent/integration.md +8 -0
- package/agent/manifest.json +2 -2
- package/dist/client/index.d.ts +15 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +8 -1
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +5 -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 +7 -5
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/reports.d.ts +3 -0
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +16 -5
- package/dist/component/reports.js.map +1 -1
- package/dist/component/sanitize.d.ts +3 -1
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +30 -1
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +3 -1
- package/dist/component/sites.d.ts +4 -0
- package/dist/component/sites.d.ts.map +1 -1
- package/dist/component/sites.js +13 -2
- package/dist/component/sites.js.map +1 -1
- package/dist/component/validators.d.ts +3 -1
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +1 -0
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +1 -1
- package/dist/tracker/generated.js +1 -1
- package/docs/upgrading.md +27 -0
- package/package.json +1 -1
- package/src/component/_generated/component.ts +5 -0
- package/src/component/ingest.ts +8 -3
- package/src/component/reports.ts +23 -5
- package/src/component/sanitize.ts +46 -1
- package/src/component/sites.ts +14 -1
- package/src/component/validators.ts +1 -0
- package/src/tracker/generated.ts +1 -1
|
@@ -82,6 +82,36 @@ const NUMERIC_SEGMENT = /^\d+$/;
|
|
|
82
82
|
const UUID_SEGMENT =
|
|
83
83
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
84
84
|
const HEX_SEGMENT = /^[0-9a-f]{16,}$/i;
|
|
85
|
+
const ROUTE_PARAMETER = /^:[A-Za-z][A-Za-z0-9_]*$/;
|
|
86
|
+
|
|
87
|
+
/** Ordered, full-path patterns; no regex or wildcard execution from settings. */
|
|
88
|
+
export function validateRoutePatterns(patterns: readonly string[]): string[] {
|
|
89
|
+
if (patterns.length > 32)
|
|
90
|
+
throw new Error("At most 32 route patterns are allowed");
|
|
91
|
+
return [
|
|
92
|
+
...new Set(
|
|
93
|
+
patterns.map((value) => {
|
|
94
|
+
const pattern = value.trim().replace(/\/$/, "");
|
|
95
|
+
const segments = pattern.slice(1).split("/");
|
|
96
|
+
if (
|
|
97
|
+
!pattern.startsWith("/") ||
|
|
98
|
+
pattern.length > 256 ||
|
|
99
|
+
/[\s?#*\\]/.test(pattern) ||
|
|
100
|
+
!segments.some((segment) => ROUTE_PARAMETER.test(segment)) ||
|
|
101
|
+
segments.some(
|
|
102
|
+
(segment) =>
|
|
103
|
+
!segment ||
|
|
104
|
+
segment === "." ||
|
|
105
|
+
segment === ".." ||
|
|
106
|
+
(segment.includes(":") && !ROUTE_PARAMETER.test(segment)),
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
throw new Error("Use full route patterns such as /pedido/:codigo");
|
|
110
|
+
return pattern;
|
|
111
|
+
}),
|
|
112
|
+
),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
85
115
|
|
|
86
116
|
/**
|
|
87
117
|
* Collapses the identifiers that unmistakably vary per record — numbers,
|
|
@@ -91,10 +121,25 @@ const HEX_SEGMENT = /^[0-9a-f]{16,}$/i;
|
|
|
91
121
|
* spellings serve the same page. Only the map uses this; every other report
|
|
92
122
|
* keeps the sanitized path exactly as the tracker sent it.
|
|
93
123
|
*/
|
|
94
|
-
export function normalizeRoute(
|
|
124
|
+
export function normalizeRoute(
|
|
125
|
+
path: string,
|
|
126
|
+
patterns: readonly string[] = [],
|
|
127
|
+
): string {
|
|
95
128
|
const trimmed =
|
|
96
129
|
path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
97
130
|
const segments = trimmed.split("/");
|
|
131
|
+
for (const pattern of patterns) {
|
|
132
|
+
const parts = pattern.split("/");
|
|
133
|
+
if (
|
|
134
|
+
parts.length === segments.length &&
|
|
135
|
+
parts.every(
|
|
136
|
+
(part, index) =>
|
|
137
|
+
part === segments[index] ||
|
|
138
|
+
(ROUTE_PARAMETER.test(part) && Boolean(segments[index])),
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
return pattern;
|
|
142
|
+
}
|
|
98
143
|
for (let index = 1; index < segments.length; index += 1) {
|
|
99
144
|
const segment = segments[index] ?? "";
|
|
100
145
|
if (
|
package/src/component/sites.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
sanitizeCurrency,
|
|
13
13
|
sanitizeOpaqueId,
|
|
14
14
|
validateTimezone,
|
|
15
|
+
validateRoutePatterns,
|
|
15
16
|
} from "./sanitize.js";
|
|
16
17
|
import { siteFieldsValidator } from "./validators.js";
|
|
17
18
|
|
|
@@ -29,6 +30,7 @@ export const create = mutation({
|
|
|
29
30
|
timezone: v.optional(v.string()),
|
|
30
31
|
currency: v.optional(v.string()),
|
|
31
32
|
cookieless: v.optional(v.boolean()),
|
|
33
|
+
routePatterns: v.optional(v.array(v.string())),
|
|
32
34
|
},
|
|
33
35
|
returns: v.id("sites"),
|
|
34
36
|
handler: async (ctx, args) => {
|
|
@@ -41,6 +43,7 @@ export const create = mutation({
|
|
|
41
43
|
let timezone: string | undefined;
|
|
42
44
|
let networkId: string | undefined;
|
|
43
45
|
let currency: string;
|
|
46
|
+
let routePatterns: string[];
|
|
44
47
|
try {
|
|
45
48
|
domains = normalizeDomains(args.domains);
|
|
46
49
|
timezone = validateTimezone(args.timezone);
|
|
@@ -48,6 +51,7 @@ export const create = mutation({
|
|
|
48
51
|
? sanitizeOpaqueId(args.networkId, "networkId")
|
|
49
52
|
: undefined;
|
|
50
53
|
currency = sanitizeCurrency(args.currency ?? "USD");
|
|
54
|
+
routePatterns = validateRoutePatterns(args.routePatterns ?? []);
|
|
51
55
|
} catch (error) {
|
|
52
56
|
fail(
|
|
53
57
|
"INVALID_SITE",
|
|
@@ -69,7 +73,10 @@ export const create = mutation({
|
|
|
69
73
|
existing.networkId !== networkId ||
|
|
70
74
|
existing.timezone !== timezone ||
|
|
71
75
|
(existing.currency ?? "USD") !== currency ||
|
|
72
|
-
existing.cookieless !== cookieless
|
|
76
|
+
existing.cookieless !== cookieless ||
|
|
77
|
+
(args.routePatterns !== undefined &&
|
|
78
|
+
JSON.stringify(existing.routePatterns ?? []) !==
|
|
79
|
+
JSON.stringify(routePatterns))
|
|
73
80
|
) {
|
|
74
81
|
fail(
|
|
75
82
|
"CONFLICT",
|
|
@@ -99,6 +106,7 @@ export const create = mutation({
|
|
|
99
106
|
timezone,
|
|
100
107
|
currency,
|
|
101
108
|
cookieless,
|
|
109
|
+
routePatterns,
|
|
102
110
|
createdAt: now,
|
|
103
111
|
updatedAt: now,
|
|
104
112
|
});
|
|
@@ -116,6 +124,7 @@ export const update = mutation({
|
|
|
116
124
|
timezone: v.optional(v.union(v.string(), v.null())),
|
|
117
125
|
currency: v.optional(v.string()),
|
|
118
126
|
cookieless: v.optional(v.boolean()),
|
|
127
|
+
routePatterns: v.optional(v.array(v.string())),
|
|
119
128
|
},
|
|
120
129
|
returns: v.null(),
|
|
121
130
|
handler: async (ctx, args) => {
|
|
@@ -129,6 +138,7 @@ export const update = mutation({
|
|
|
129
138
|
timezone?: string | undefined;
|
|
130
139
|
currency?: string;
|
|
131
140
|
cookieless?: boolean;
|
|
141
|
+
routePatterns?: string[];
|
|
132
142
|
updatedAt: number;
|
|
133
143
|
} = { updatedAt: Date.now() };
|
|
134
144
|
if (args.name !== undefined) {
|
|
@@ -161,6 +171,9 @@ export const update = mutation({
|
|
|
161
171
|
if (args.currency !== undefined) {
|
|
162
172
|
patch.currency = sanitizeCurrency(args.currency);
|
|
163
173
|
}
|
|
174
|
+
if (args.routePatterns !== undefined) {
|
|
175
|
+
patch.routePatterns = validateRoutePatterns(args.routePatterns);
|
|
176
|
+
}
|
|
164
177
|
} catch (error) {
|
|
165
178
|
fail(
|
|
166
179
|
"INVALID_SITE",
|
|
@@ -161,6 +161,7 @@ export const siteFieldsValidator = v.object({
|
|
|
161
161
|
timezone: v.optional(v.string()),
|
|
162
162
|
currency: v.optional(v.string()),
|
|
163
163
|
cookieless: v.boolean(),
|
|
164
|
+
routePatterns: v.optional(v.array(v.string())),
|
|
164
165
|
createdAt: v.number(),
|
|
165
166
|
updatedAt: v.number(),
|
|
166
167
|
});
|
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.7.0";
|
|
3
3
|
export const TRACKER_SOURCE = "(()=>{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()+\"\",r=new URLSearchParams(location.search),s=r.get(\"ref\")?.slice(0,64);let o;for(const t of[\"source\",\"medium\",\"campaign\",\"term\",\"content\"]){const e=r.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=>r.has(t)).slice(0,8);let c,d,l,h,f=t.dataset.visitor||\"\",p=Boolean(f);try{f||=localStorage._rv||=n()}catch{}try{d=sessionStorage,l=d._r,c=d._ri==f&&d._rt,h=s||d._a,h&&(d._a=h),o||=d._u&&JSON.parse(d._u),o&&(d._u=JSON.stringify(o))}catch{h=s}let v,g=0;const u=[];let m=document.referrer.split(\"/\",3).join(\"/\")||void 0;const _=t=>{const n=u.splice(0,50);if(!n.length)return;const r=()=>new Blob([JSON.stringify({siteId:e,events:n,sentAt:Date.now()})]);let s=r();for(;s.size>64e3&&n.length>1;)u.unshift(n.pop()),s=r();t&&navigator.sendBeacon(i,s)||fetch(i,{method:\"POST\",body:s,keepalive:1}).catch(()=>{}),u.length&&_(t)},y=(t,e,i)=>{const r=Date.now(),s=r-c;c=r,s<18e5||(l=n(),\"pageview\"!=t&&(S=\"\",w()));try{d._r=l,d._ri=f,d._rt=r}catch{}u.push({eventId:`${l}.${r.toString(36)}.${g}`,sessionId:l,visitorId:f||l,type:t,name:e,path:location.pathname.slice(0,256),referrer:m,timestamp:r,sequence:g++,affiliateSlug:h,...i}),clearTimeout(v),v=setTimeout(_,1200)};let S;const w=()=>{const t=location.pathname;t!=S&&(S=t,y(\"pageview\",void 0,{properties:o,clid:a.length?a:void 0}))};window.rastro=(e,i,r)=>{if(\"context\"==e)return{sessionId:l,visitorId:f||l,identified:p};if(\"identify\"==e||\"reset\"==e){if(\"identify\"==e&&(!i||i.length>128||!/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(i)))return;if(\"identify\"==e&&i==f)return;_();const r=\"reset\"==e||p;if(p=\"identify\"==e,f=\"identify\"==e?i:n(),t.dataset.visitor=\"identify\"==e?f:\"\",r){h=void 0,o=void 0,m=void 0,a.length=0;try{\"reset\"==e&&(localStorage._rv=f),d._a=\"\",d._u=\"\"}catch{}}c=0,g=0,S=\"\",w()}\"event\"==e&&y(\"custom\",`${i||\"event\"}`.slice(0,80),r&&JSON.stringify(r).length>2e4?void 0:{properties:r})},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,r=n&&new URL(n),s=/^https?:$/.test(r?.protocol),o=s&&r.origin!=location.origin;y(i?\"custom\":o?\"outbound\":\"click\",i,{target:(e.dataset.rastroLabel||e.innerText||e.tagName).slice(0,64),href:s?((o?r.origin:\"\")+r.pathname).slice(0,256):void 0})},1);for(const t of[\"pushState\",\"replaceState\"]){const e=history[t];history[t]=(...t)=>{e.apply(history,t),w()}}addEventListener(\"popstate\",w);let b=0;const I=()=>{y(\"heartbeat\"),_()},L=()=>{b&&(clearInterval(b),b=0,y(\"leave\"),_(1))},E=()=>{document.hidden?L():b||(I(),b=setInterval(I,2e4))};addEventListener(\"pagehide\",L),addEventListener(\"pageshow\",E),document.addEventListener(\"visibilitychange\",E),b=setInterval(I,2e4),w(),E()})();";
|
|
4
4
|
export const TRACKER_GZIP_BASE64 = "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";
|
|
5
5
|
export const TRACKER_RAW_BYTES = 2999;
|