@iann29/rastro 0.3.0 → 0.4.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 +80 -36
- package/agent/manifest.json +2 -2
- package/dist/component/_generated/component.d.ts +1 -0
- 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 +7 -2
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +24 -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/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 +14 -1
- 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 +34 -0
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +69 -17
- 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/reports.d.ts.map +1 -1
- package/dist/component/reports.js +21 -6
- 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/schema.d.ts +6 -2
- package/dist/component/schema.js +5 -2
- package/dist/component/schema.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.js +88 -43
- 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 +84 -28
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/component/_generated/component.ts +1 -0
- package/src/component/_generated/server.ts +1 -0
- package/src/component/constants.ts +24 -3
- package/src/component/convex.config.ts +3 -0
- package/src/component/geo.ts +1 -1
- package/src/component/http.ts +15 -1
- package/src/component/identity.ts +41 -2
- package/src/component/ingest.ts +92 -32
- package/src/component/live.ts +51 -4
- package/src/component/reports.ts +24 -8
- package/src/component/rollupStore.ts +51 -20
- package/src/component/rollups.ts +141 -32
- package/src/component/schema.ts +6 -3
- package/src/tracker/generated.ts +11 -11
package/src/component/rollups.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { v } from "convex/values";
|
|
1
|
+
import { v, type Infer } from "convex/values";
|
|
2
2
|
import { internal } from "./_generated/api.js";
|
|
3
|
+
import type { Id } from "./_generated/dataModel.js";
|
|
4
|
+
import type { MutationCtx } from "./_generated/server.js";
|
|
3
5
|
import { internalMutation } from "./_generated/server.js";
|
|
4
6
|
import {
|
|
7
|
+
ROLLUP_DEAD_LETTER_RETENTION_MS,
|
|
5
8
|
ROLLUP_FOLD_BATCH_DOCS,
|
|
9
|
+
ROLLUP_FOLD_DEBOUNCE_MS,
|
|
6
10
|
ROLLUP_FOLD_LEASE_MS,
|
|
11
|
+
ROLLUP_FOLD_MAX_FAILURES,
|
|
7
12
|
ROLLUP_FOLD_MAX_ROWS,
|
|
8
13
|
} from "./constants.js";
|
|
9
14
|
import {
|
|
@@ -13,24 +18,65 @@ import {
|
|
|
13
18
|
mergeQueuedRollups,
|
|
14
19
|
} from "./rollupStore.js";
|
|
15
20
|
|
|
21
|
+
const foldArgs = { siteId: v.id("sites"), token: v.string() };
|
|
22
|
+
const foldResult = v.object({
|
|
23
|
+
folded: v.number(),
|
|
24
|
+
remaining: v.boolean(),
|
|
25
|
+
stale: v.boolean(),
|
|
26
|
+
});
|
|
27
|
+
type FoldResult = Infer<typeof foldResult>;
|
|
28
|
+
|
|
29
|
+
// A failed run's reads still count against the transaction that catches it,
|
|
30
|
+
// so a run stops short of the platform's read limits (16 MiB, 32,000
|
|
31
|
+
// documents, 4,096 queries) and leaves room to record the failure.
|
|
32
|
+
const RUN_READ_LIMITS = {
|
|
33
|
+
bytesRead: 15 * 1024 * 1024,
|
|
34
|
+
documentsRead: 31_000,
|
|
35
|
+
databaseQueries: 4_000,
|
|
36
|
+
};
|
|
37
|
+
|
|
16
38
|
/**
|
|
17
39
|
* Folds a site's queued report rollup deltas into the rollup rows. Exactly
|
|
18
|
-
* one chain runs per site:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
40
|
+
* one chain runs per site: a batch that finds the lease free takes it and
|
|
41
|
+
* schedules the first run a debounce away (see ensureRollupFold), every run
|
|
42
|
+
* carries the token of that lease, and a run whose token is no longer
|
|
43
|
+
* current exits without touching anything. Batches that land while the
|
|
44
|
+
* lease is held only append to the queue; they never write the lease or
|
|
45
|
+
* schedule anything.
|
|
46
|
+
*
|
|
47
|
+
* A run folds the batches created before it began and, if it folded any,
|
|
48
|
+
* schedules the next run a debounce later (at once while a backlog remains),
|
|
49
|
+
* so a steady stream costs one run per second and batches landing during a
|
|
50
|
+
* run never conflict with it. Only a run that finds nothing left releases
|
|
51
|
+
* the lease, and that run reads the whole queue range: a batch landing
|
|
52
|
+
* concurrently either commits first, and the run retries and folds it, or
|
|
53
|
+
* reads the released lease and starts a new chain.
|
|
54
|
+
*
|
|
55
|
+
* A run executes as a nested transaction, so one that throws is rolled back
|
|
56
|
+
* whole and its batches stay queued; the oldest is charged the failure and
|
|
57
|
+
* the next run retries it alone. After ROLLUP_FOLD_MAX_FAILURES runs in a
|
|
58
|
+
* row it is set aside as a dead letter and the queue moves on.
|
|
26
59
|
*/
|
|
27
60
|
export const fold = internalMutation({
|
|
28
|
-
args:
|
|
29
|
-
returns:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
61
|
+
args: foldArgs,
|
|
62
|
+
returns: foldResult,
|
|
63
|
+
handler: async (ctx, args) => {
|
|
64
|
+
try {
|
|
65
|
+
const result: FoldResult = await ctx.runMutation(
|
|
66
|
+
internal.rollups.foldRun,
|
|
67
|
+
args,
|
|
68
|
+
{ transactionLimits: RUN_READ_LIMITS },
|
|
69
|
+
);
|
|
70
|
+
return result;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return await setAsideFailedRun(ctx, args, error);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const foldRun = internalMutation({
|
|
78
|
+
args: foldArgs,
|
|
79
|
+
returns: foldResult,
|
|
34
80
|
handler: async (ctx, args) => {
|
|
35
81
|
const state = await ctx.db
|
|
36
82
|
.query("rollupFoldStates")
|
|
@@ -42,20 +88,31 @@ export const fold = internalMutation({
|
|
|
42
88
|
const now = Date.now();
|
|
43
89
|
const queued = await ctx.db
|
|
44
90
|
.query("reportRollupQueue")
|
|
45
|
-
.withIndex("
|
|
46
|
-
range
|
|
91
|
+
.withIndex("by_siteId_and_deadLetteredAt_and_createdAt", (range) =>
|
|
92
|
+
range
|
|
93
|
+
.eq("siteId", args.siteId)
|
|
94
|
+
.eq("deadLetteredAt", undefined)
|
|
95
|
+
.lte("createdAt", now),
|
|
47
96
|
)
|
|
48
|
-
.order("asc")
|
|
49
97
|
.take(ROLLUP_FOLD_BATCH_DOCS + 1);
|
|
50
98
|
if (queued.length === 0) {
|
|
51
|
-
if (
|
|
99
|
+
if (await oldestQueued(ctx, args.siteId)) {
|
|
100
|
+
await ctx.scheduler.runAfter(
|
|
101
|
+
ROLLUP_FOLD_DEBOUNCE_MS,
|
|
102
|
+
internal.rollups.fold,
|
|
103
|
+
args,
|
|
104
|
+
);
|
|
105
|
+
} else if (state.leaseUntil > now) {
|
|
52
106
|
await ctx.db.patch("rollupFoldStates", state._id, { leaseUntil: now });
|
|
53
107
|
}
|
|
54
108
|
return { folded: 0, remaining: false, stale: false };
|
|
55
109
|
}
|
|
110
|
+
// A batch that already failed a run is retried alone, so the next
|
|
111
|
+
// failure is charged to the batch that causes it.
|
|
112
|
+
const batchDocs = queued[0].foldFailures ? 1 : ROLLUP_FOLD_BATCH_DOCS;
|
|
56
113
|
const deltas = createReportRollupDeltas();
|
|
57
114
|
let taken = 0;
|
|
58
|
-
for (const document of queued.slice(0,
|
|
115
|
+
for (const document of queued.slice(0, batchDocs)) {
|
|
59
116
|
mergeQueuedRollups(deltas, document);
|
|
60
117
|
taken += 1;
|
|
61
118
|
if (countReportRollupRows(deltas) >= ROLLUP_FOLD_MAX_ROWS) break;
|
|
@@ -64,19 +121,71 @@ export const fold = internalMutation({
|
|
|
64
121
|
for (const document of queued.slice(0, taken)) {
|
|
65
122
|
await ctx.db.delete("reportRollupQueue", document._id);
|
|
66
123
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
leaseUntil: now + ROLLUP_FOLD_LEASE_MS,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
await ctx.scheduler.runAfter(0, internal.rollups.fold, args);
|
|
77
|
-
} else {
|
|
78
|
-
await ctx.db.patch("rollupFoldStates", state._id, { leaseUntil: now });
|
|
124
|
+
// Renew only when the lease is halfway gone, so the state document is
|
|
125
|
+
// rewritten about twice a minute while batches keep arriving.
|
|
126
|
+
if (state.leaseUntil - now < ROLLUP_FOLD_LEASE_MS / 2) {
|
|
127
|
+
await ctx.db.patch("rollupFoldStates", state._id, {
|
|
128
|
+
leaseUntil: now + ROLLUP_FOLD_LEASE_MS,
|
|
129
|
+
});
|
|
79
130
|
}
|
|
131
|
+
const remaining = queued.length > taken;
|
|
132
|
+
await ctx.scheduler.runAfter(
|
|
133
|
+
remaining ? 0 : ROLLUP_FOLD_DEBOUNCE_MS,
|
|
134
|
+
internal.rollups.fold,
|
|
135
|
+
args,
|
|
136
|
+
);
|
|
80
137
|
return { folded: taken, remaining, stale: false };
|
|
81
138
|
},
|
|
82
139
|
});
|
|
140
|
+
|
|
141
|
+
/** The oldest batch still waiting to fold; dead letters are skipped. */
|
|
142
|
+
function oldestQueued(ctx: MutationCtx, siteId: Id<"sites">) {
|
|
143
|
+
return ctx.db
|
|
144
|
+
.query("reportRollupQueue")
|
|
145
|
+
.withIndex("by_siteId_and_deadLetteredAt_and_createdAt", (range) =>
|
|
146
|
+
range.eq("siteId", siteId).eq("deadLetteredAt", undefined),
|
|
147
|
+
)
|
|
148
|
+
.first();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function setAsideFailedRun(
|
|
152
|
+
ctx: MutationCtx,
|
|
153
|
+
args: { siteId: Id<"sites">; token: string },
|
|
154
|
+
error: unknown,
|
|
155
|
+
): Promise<FoldResult> {
|
|
156
|
+
const state = await ctx.db
|
|
157
|
+
.query("rollupFoldStates")
|
|
158
|
+
.withIndex("by_siteId", (range) => range.eq("siteId", args.siteId))
|
|
159
|
+
.unique();
|
|
160
|
+
const head = await oldestQueued(ctx, args.siteId);
|
|
161
|
+
if (!head || state?.token !== args.token) throw error;
|
|
162
|
+
const now = Date.now();
|
|
163
|
+
const foldFailures = (head.foldFailures ?? 0) + 1;
|
|
164
|
+
if (foldFailures < ROLLUP_FOLD_MAX_FAILURES) {
|
|
165
|
+
await ctx.db.patch("reportRollupQueue", head._id, { foldFailures });
|
|
166
|
+
} else {
|
|
167
|
+
console.error(
|
|
168
|
+
`rollup fold set aside queued batch ${head._id} of site ${args.siteId} after ${foldFailures} failed runs`,
|
|
169
|
+
error,
|
|
170
|
+
);
|
|
171
|
+
await ctx.db.patch("reportRollupQueue", head._id, {
|
|
172
|
+
foldFailures,
|
|
173
|
+
deadLetteredAt: now,
|
|
174
|
+
});
|
|
175
|
+
// Dead letters stay for inspection; each new one retires expired ones.
|
|
176
|
+
const expired = await ctx.db
|
|
177
|
+
.query("reportRollupQueue")
|
|
178
|
+
.withIndex("by_siteId_and_deadLetteredAt_and_createdAt", (range) =>
|
|
179
|
+
range
|
|
180
|
+
.eq("siteId", args.siteId)
|
|
181
|
+
.gte("deadLetteredAt", 0)
|
|
182
|
+
.lt("deadLetteredAt", now - ROLLUP_DEAD_LETTER_RETENTION_MS),
|
|
183
|
+
)
|
|
184
|
+
.take(ROLLUP_FOLD_BATCH_DOCS);
|
|
185
|
+
for (const document of expired) {
|
|
186
|
+
await ctx.db.delete("reportRollupQueue", document._id);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
await ctx.scheduler.runAfter(0, internal.rollups.fold, args);
|
|
190
|
+
return { folded: 0, remaining: true, stale: false };
|
|
191
|
+
}
|
package/src/component/schema.ts
CHANGED
|
@@ -248,10 +248,11 @@ export default defineSchema({
|
|
|
248
248
|
.index("by_siteId_and_bucketStart", ["siteId", "bucketStart"]),
|
|
249
249
|
|
|
250
250
|
// Rollup deltas wait here between ingestion and the fold job; see
|
|
251
|
-
//
|
|
251
|
+
// rollups.ts. Rows are deleted as they are folded; a batch that keeps
|
|
252
|
+
// failing is set aside with `deadLetteredAt` and skipped.
|
|
252
253
|
reportRollupQueue: defineTable(reportRollupQueueFields).index(
|
|
253
|
-
"
|
|
254
|
-
["siteId", "createdAt"],
|
|
254
|
+
"by_siteId_and_deadLetteredAt_and_createdAt",
|
|
255
|
+
["siteId", "deadLetteredAt", "createdAt"],
|
|
255
256
|
),
|
|
256
257
|
|
|
257
258
|
rollupFoldStates: defineTable(rollupFoldStateFields).index("by_siteId", [
|
|
@@ -441,6 +442,8 @@ export default defineSchema({
|
|
|
441
442
|
|
|
442
443
|
ingestWindows: defineTable({
|
|
443
444
|
siteId: v.id("sites"),
|
|
445
|
+
// A session ID, or `~client:` plus a salted daily client-network key for
|
|
446
|
+
// the per-client budget; no sanitized session ID starts with `~`.
|
|
444
447
|
sessionId: v.string(),
|
|
445
448
|
windowStart: v.number(),
|
|
446
449
|
eventCount: v.number(),
|
package/src/tracker/generated.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated by scripts/build-tracker.mjs. Do not edit.
|
|
2
|
-
export const RASTRO_VERSION = "0.
|
|
3
|
-
export const TRACKER_SOURCE = "(()=>{const e=document.currentScript,t=e?.dataset.site,
|
|
4
|
-
export const TRACKER_GZIP_BASE64 = "
|
|
5
|
-
export const TRACKER_RAW_BYTES =
|
|
6
|
-
export const TRACKER_GZIP_BYTES =
|
|
7
|
-
export const TRACKER_HASH = "
|
|
8
|
-
export const VITALS_SOURCE = "(()=>{const t=document.currentScript,e=t?.dataset.site,n=t?.dataset.endpoint||t?.src&&new URL(\"events\",t.src).href;if(!e||!n)return;const r=()=>crypto.randomUUID?.()||Math.random()+\"\";let a;try{a=sessionStorage._r||r(),sessionStorage._r=a}catch{a=r()}const s
|
|
9
|
-
export const VITALS_GZIP_BASE64 = "
|
|
10
|
-
export const VITALS_RAW_BYTES =
|
|
11
|
-
export const VITALS_GZIP_BYTES =
|
|
12
|
-
export const VITALS_HASH = "
|
|
2
|
+
export const RASTRO_VERSION = "0.4.0";
|
|
3
|
+
export const TRACKER_SOURCE = "(()=>{const e=document.currentScript,t=e?.dataset.site,n=e?.dataset.endpoint||e?.src&&new URL(\"events\",e.src).href;if(!t||!n)return;const r=()=>crypto.randomUUID?.()||Math.random()+\"\",o=new URLSearchParams(location.search),s=o.get(\"ref\")?.slice(0,64);let a;for(const e of[\"source\",\"medium\",\"campaign\",\"term\",\"content\"]){const t=o.get(\"utm_\"+e)?.slice(0,64);t&&((a??={})[\"utm_\"+e]=t)}let i,c,d,l,h=e.dataset.visitor||\"\";try{h||=localStorage._rv||=r()}catch{}try{c=sessionStorage,d=c._r,i=c._ri==h&&c._rt,l=s||c._a,l&&(c._a=l),a||=c._u&&JSON.parse(c._u),a&&(c._u=JSON.stringify(a))}catch{l=s}let p,u=0;const v=[],g=document.referrer?new URL(document.referrer).origin:void 0,m=e=>{const r=v.splice(0,50);if(!r.length)return;const o=()=>new Blob([JSON.stringify({siteId:t,events:r,sentAt:Date.now()})]);let s=o();for(;s.size>64e3&&r.length>1;)v.unshift(r.pop()),s=o();e&&navigator.sendBeacon(n,s)||fetch(n,{method:\"POST\",body:s,keepalive:1}).catch(()=>{}),v.length&&m(e)},f=(e,t,n)=>{const o=Date.now(),s=o-i;i=o,s<18e5||(d=r(),\"pageview\"!=e&&(y=\"\",_()));try{c._r=d,c._ri=h,c._rt=o}catch{}v.push({eventId:`${d}.${o.toString(36)}.${u}`,sessionId:d,visitorId:h||d,type:e,name:t,path:location.pathname.slice(0,256),referrer:g,timestamp:o,sequence:u++,affiliateSlug:l,...n}),clearTimeout(p),p=setTimeout(m,1200)};let y;const _=()=>{const e=location.pathname;e!=y&&(y=e,f(\"pageview\",void 0,a&&{properties:a}))};window.rastro=(e,t,n)=>{if(\"context\"==e)return{sessionId:d,visitorId:h||d};\"event\"==e&&f(\"custom\",`${t||\"event\"}`.slice(0,80),n&&JSON.stringify(n).length>2e4?void 0:{properties:n})},document.addEventListener(\"click\",e=>{const t=e.target?.closest(\"a,button,[data-rastro-event]\");if(!t)return;const n=t.dataset.rastroEvent,r=t.href,o=r&&new URL(r),s=/^https?:$/.test(o?.protocol),a=s&&o.origin!=location.origin;f(n?\"custom\":a?\"outbound\":\"click\",n,{target:(t.dataset.rastroLabel||t.innerText||t.tagName).slice(0,64),href:s?((a?o.origin:\"\")+o.pathname).slice(0,256):void 0})},1);for(const e of[\"pushState\",\"replaceState\"]){const t=history[e];history[e]=(...e)=>{t.apply(history,e),_()}}addEventListener(\"popstate\",_);let S=0;const w=()=>{f(\"heartbeat\"),m()},I=()=>{S&&(clearInterval(S),S=0,f(\"leave\"),m(1))},L=()=>{document.hidden?I():S||(w(),S=setInterval(w,2e4))};addEventListener(\"pagehide\",I),addEventListener(\"pageshow\",L),document.addEventListener(\"visibilitychange\",L),S=setInterval(w,2e4),_(),L()})();";
|
|
4
|
+
export const TRACKER_GZIP_BASE64 = "H4sIAAAAAAACA32VUW/bNhDHv0pCBAKJXFmnTYNCHmus6B48ZG0xt09BljDS2SIqkRp5sutZ+u4DJdlOmm5PEnkn3fH4+99xLtS7XeZsoBNUucuaCi3JrPEeLS0yb2oCUjiTuSYdkGQwhGAf76DNa2cstS3OZPBZkljcnHz985ozXKOlwADjvpCFx+XULPkpte2pFR6p8XY6RPcqppL5bU1Oem1zV339Ov8wk1y07R+ainGTi3PGwKkxxgK1z4rP2usq8NJlmoyzMvS7AoJycoXEmcclEzMZSpMhn8DVpZiWSCd6unSej8c/ccsbFlzjM2TAKsxNUzFgma5qbVaWASP0/Y6zhJbYrRgrR/swDVV37Bx/iERJwrmezdSuEzd7n1tFoos5GMgghxIKhYeSrk0w5HzbMjYlv90Vbavi4coFOa9XKO/8um2V56LLNGXFrotemQoYgnF29IJcZfLOg+kfRqkiSeIbQalC22byTkOZJDy+qFKAbtvo2STJ74tPH2WtfcBobATowa1RvSWQN3ZllluuxT6DUoX+ODU0ajLe6Vrd3MLqiJXHJXqPfrYH5JlFSOfNyth07Ux+MoFK4YFPr9Yy1GNd30xET5KXJdoVFU9hcj1MMcr70j3wmx+y3kWI53lKMACaegho6VdKP2hCad2Gi07cDowE5bjoOZkGGcw/+O7qEl8nyT7yu4upWMvGhsIsiXtZu5qLHj0uppgkVq/NSpPzMqDN36POnOUWgmjbJVJWcAu7Cqlweco+f1p8YfDg8m0a4BtirUuzxvSiE7KvMu/l2glYj8GTpOIoOlgqjkBgj2p26niWmMwLMzXKQfjl4i2+aVueR3qA1XqFa4MbdqowSfhWMQZ3XAjRcxdpUTkM+BT9k5TbM7eWdRMKvuuLOM/T+7Nd3smznZPkFn2x+esrEXea7h5GNud5msPI9zxPi7bNgbY1pghWV5gS1JqK9CDluIqGg6JevbkSsOclXQGZCgPpqk4dBPy7QZth2pyfg14uTWk04aJsVmkJUkrbCchK1P6LqdA1xGsBtQpI+3UFF68mE9H1N78dabpTT7rks9SmeKq2ffEQlvxYUhgh1kmyq72r0ZPBkOpOiG66MTZ3G+l1IO8e3Z5Z8qHBfCemFI5c7/67et10aLPRO0ni100gVzG4P9tR247G7v5QwLcTAXbU+FETVux5foWXsyHz9HHathMdHASr8/y3+ONrEwgtes6y0mTfGBz1Sgolab9CmsmsdAEDcabhoSFyFm5ir3sxHP9Fn+MtGyRNT7VsFR364uDeBwavqB8o4JQ/jhwfYX/5V0FUh1l69lJSDOtmsvaOXOZim1MhSdzYaE6P1zlsTJfczvY1TPWMuYYeXGNzlu6PaGE3nCvlP6Z2rR+wbFuSxlr0X/A7xQXp1UddoXg8FSDmnoZZnAz7ZFLGxLk7cCWeMD+2xHgLF+LZ3IpKXJCmOLo81qXOcFg+GlKFCeT89gZvp8dXxaWUGMkjqeu63PLRBihiJ+i651dduzoMse6GHrk4tPzNoJUlZwVqTw+oiQmouOhgPpgWcZREDc4toV/rki8ELNQkSqdEvcbe/0KIDq6HLw7QFSbP0c7mXKSLtuWxsy2ieg9/2sArvIzq+knOeoWFyZHBXMDPzaFwGwbX4v8oj9p7MKWhbVZou8Le/2dJxNrBdZwjXEz/BQqEd9NnCQAA";
|
|
5
|
+
export const TRACKER_RAW_BYTES = 2407;
|
|
6
|
+
export const TRACKER_GZIP_BYTES = 1278;
|
|
7
|
+
export const TRACKER_HASH = "7fccc2fba6e092cb";
|
|
8
|
+
export const VITALS_SOURCE = "(()=>{const t=document.currentScript,e=t?.dataset.site,n=t?.dataset.endpoint||t?.src&&new URL(\"events\",t.src).href;if(!e||!n)return;const r=()=>crypto.randomUUID?.()||Math.random()+\"\";let 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)})();";
|
|
9
|
+
export const VITALS_GZIP_BASE64 = "H4sIAAAAAAACA3VVbW/bNhD+K6lQCORKc/ayFINcJlj6AnjImqBOPwXBSosniatEauTJnSbpvw+i7MSJ22/SkXf33N1zDwmh4rxLrfF4gkLZtKnAIE8b58DgOnW6RgYCL7iSKD0g9xqBmUMLGFVbbbDv8YJ7l8axgW8nnz9dkQi2YNBHDEc75YWDbKkz8gL6/oWhDrBxZjlld2KEkrq2RsudNMpWnz+v3l1wQvv+T4nFzkjoqyhaloAnXuADhq32Gq3r+yhaoms73/eitKks12idzIH/5bZ9LxyhQyoxLbphymrFO4nAjf1GaAgqg7sUHrzX1jy6ayGEj2M7OzrBN4vf4CyOjw763hHKjsxCsu9E98dGFHaHVk7IA2QtfN9Llob6UFvDa4mFkRVwX+oUyJz9cvaaMiW6gRXi7p6VgiADZsZRj9XtGz6O6QZcZl0lTQrXGw9uC46gOAeCPAd8b9Bp8IRSunTcThdIh20NCbJNk2XgQCULxjk3A2UFrxtfkG530yWOFdKoElwCw0PrB5aJ+jHvQaLL9rat4YKTyMitzkN5Eb2b3y+zOM64A19b42GN0uH5PI6J4re3Hy7FsyPKShLVUhuMGIrzLrOOTEXDic1OkEaZdh5nqTUIBrOmnE23hRDAx1aG0B/e3gjgfox4qyugQ4hbSpfD95xDql0agXfISzA5FrPF/XKKd3UUL1CuEnPWiDnbivkyxG9tgzNf6OxH8IEXUn2CFAyuTN1g35Mqjg9Cz7ZvFnD61NS8OYPTi+qVAL6VZQMJqfafrHmCi20Pf5nib6/WIqxgJf8l4bfv52wBpz9VdKxiApePXKsDq9aAbC1+gF0bBCfTcbYrFcek5lIp8sxOWT5xCbhqXCACpUE78l1f9/KRc28dkpHiVJzDDEfX6coOtDZkb2GL+R4uPOHgQfK3thnFrOZe/wdLxVcfb0R+dxRqtmDBlpXWOgI/n80pvR/GCQbVi9iadXvot4UDX9hSJb/OJxZNBNTj9CK2nojQivkO2yaIYacz0u7LfGjk43LhwXLZ7KSg49ai/AqfILVOjWvLkCvtU2sMpEj2peOB6jEnrjd/Q4r8K7SeKMorWRMyyQXpQi0rlXx52cmBv+yQo12j0yYnp6/pwLcvOzN82WvXSiWS7bR4pRLNglJEW42yjNi4WAmwiX2TpNvGKKLu4J6yUcWSlKGuwKOs6gSZh38aMCkkZtgN3z0bfisWu5p8IN5laTfk7o/19UfuA0qdtaQb36yVSoBN71HimAeDv2OCA72nzIqd2ljHPRh1CTK15oJvtFHk4YgubRxbYpinfZ8BpgUxrKsAC6uS6OZ6fRuxjVVt4tlXgFqWegvJYqA8qB4JAx3osHx4ZKVS70c8V9ojGHAkGju30aXGNi2kySFiwSsqtFJgRnV6cH68ukaJEMcbMurTccxa5lBoBRHb0IESuvwfweV+3/MHAAA=";
|
|
10
|
+
export const VITALS_RAW_BYTES = 2035;
|
|
11
|
+
export const VITALS_GZIP_BYTES = 1097;
|
|
12
|
+
export const VITALS_HASH = "3dd7a68d5212c1f8";
|