@iann29/rastro 0.1.0-alpha.1 → 0.1.0-alpha.11
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 +693 -69
- package/agent/integration.md +801 -0
- package/agent/manifest.json +205 -0
- package/agent/manifest.schema.json +444 -0
- package/dist/client/federation.d.ts +381 -0
- package/dist/client/federation.d.ts.map +1 -0
- package/dist/client/federation.js +274 -0
- package/dist/client/federation.js.map +1 -0
- package/dist/client/index.d.ts +2673 -15
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +565 -33
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +22 -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 +315 -4
- 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/affiliates.d.ts.map +1 -1
- package/dist/component/affiliates.js +6 -2
- package/dist/component/affiliates.js.map +1 -1
- package/dist/component/cardinality.d.ts +12 -0
- package/dist/component/cardinality.d.ts.map +1 -0
- package/dist/component/cardinality.js +94 -0
- package/dist/component/cardinality.js.map +1 -0
- package/dist/component/constants.d.ts +28 -1
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +44 -1
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +6 -1
- package/dist/component/convex.config.js +9 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/coverage.d.ts +27 -0
- package/dist/component/coverage.d.ts.map +1 -0
- package/dist/component/coverage.js +56 -0
- package/dist/component/coverage.js.map +1 -0
- 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/errors.d.ts +1 -1
- package/dist/component/errors.d.ts.map +1 -1
- package/dist/component/errors.js.map +1 -1
- package/dist/component/eventStore.d.ts +21 -9
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/eventStore.js +142 -152
- package/dist/component/eventStore.js.map +1 -1
- package/dist/component/funnels.d.ts.map +1 -1
- package/dist/component/funnels.js +5 -3
- package/dist/component/funnels.js.map +1 -1
- package/dist/component/geo.d.ts +73 -0
- package/dist/component/geo.d.ts.map +1 -0
- package/dist/component/geo.js +648 -0
- package/dist/component/geo.js.map +1 -0
- package/dist/component/goals.d.ts.map +1 -1
- package/dist/component/goals.js +8 -5
- package/dist/component/goals.js.map +1 -1
- package/dist/component/guards.d.ts.map +1 -1
- package/dist/component/guards.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +281 -62
- package/dist/component/http.js.map +1 -1
- package/dist/component/identity.d.ts +13 -0
- package/dist/component/identity.d.ts.map +1 -0
- package/dist/component/identity.js +58 -0
- package/dist/component/identity.js.map +1 -0
- package/dist/component/ingest.d.ts +3 -1
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +498 -95
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +7 -6
- package/dist/component/live.js.map +1 -1
- package/dist/component/localTime.d.ts +25 -0
- package/dist/component/localTime.d.ts.map +1 -0
- package/dist/component/localTime.js +126 -0
- package/dist/component/localTime.js.map +1 -0
- package/dist/component/reports.d.ts +266 -10
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +1243 -137
- package/dist/component/reports.js.map +1 -1
- package/dist/component/retention.d.ts +75 -1
- package/dist/component/retention.d.ts.map +1 -1
- package/dist/component/retention.js +561 -38
- package/dist/component/retention.js.map +1 -1
- package/dist/component/rollupStore.d.ts +320 -0
- package/dist/component/rollupStore.d.ts.map +1 -0
- package/dist/component/rollupStore.js +596 -0
- package/dist/component/rollupStore.js.map +1 -0
- package/dist/component/rollups.d.ts +20 -0
- package/dist/component/rollups.d.ts.map +1 -0
- package/dist/component/rollups.js +73 -0
- package/dist/component/rollups.js.map +1 -0
- package/dist/component/sanitize.d.ts +24 -1
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +96 -16
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +687 -65
- package/dist/component/schema.js +187 -20
- package/dist/component/schema.js.map +1 -1
- package/dist/component/sites.d.ts +12 -0
- package/dist/component/sites.d.ts.map +1 -1
- package/dist/component/sites.js +41 -7
- package/dist/component/sites.js.map +1 -1
- package/dist/component/useragent.d.ts +9 -0
- package/dist/component/useragent.d.ts.map +1 -0
- package/dist/component/useragent.js +152 -0
- package/dist/component/useragent.js.map +1 -0
- package/dist/component/validators.d.ts +124 -69
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +35 -14
- package/dist/component/validators.js.map +1 -1
- package/dist/component/visitors.d.ts +19 -0
- package/dist/component/visitors.d.ts.map +1 -0
- package/dist/component/visitors.js +86 -0
- package/dist/component/visitors.js.map +1 -0
- package/dist/component/vitals.d.ts +41 -0
- package/dist/component/vitals.d.ts.map +1 -0
- package/dist/component/vitals.js +115 -0
- package/dist/component/vitals.js.map +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/tracker/generated.d.ts +11 -4
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +11 -4
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -1
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +57 -20
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker/vitals.d.ts +10 -0
- package/dist/tracker/vitals.d.ts.map +1 -0
- package/dist/tracker/vitals.js +140 -0
- package/dist/tracker/vitals.js.map +1 -0
- package/dist/tracker.min.js +1 -1
- package/dist/vitals.min.js +1 -0
- package/docs/benchmarks/2026-08-20-realistic.md +76 -76
- package/docs/benchmarks/2026-08-21-formal-certification.md +353 -0
- package/docs/benchmarks/2026-08-30-alpha6-recertification.md +206 -0
- package/docs/federation-setup.md +464 -0
- package/docs/federation.md +352 -0
- package/docs/upgrading.md +344 -0
- package/llms.txt +72 -0
- package/package.json +55 -11
- package/scripts/benchmark-ingest.mjs +175 -73
- package/scripts/generate-federation-keys.mjs +20 -0
- package/src/component/_generated/api.ts +22 -0
- package/src/component/_generated/component.ts +381 -4
- package/src/component/_generated/server.ts +4 -0
- package/src/component/affiliates.ts +20 -5
- package/src/component/cardinality.ts +117 -0
- package/src/component/constants.ts +44 -1
- package/src/component/convex.config.ts +11 -1
- package/src/component/coverage.ts +71 -0
- package/src/component/diagnostics.ts +65 -0
- package/src/component/errors.ts +2 -1
- package/src/component/eventStore.ts +217 -193
- package/src/component/funnels.ts +19 -16
- package/src/component/geo.ts +835 -0
- package/src/component/goals.ts +29 -21
- package/src/component/guards.ts +3 -1
- package/src/component/http.ts +404 -70
- package/src/component/identity.ts +74 -0
- package/src/component/ingest.ts +894 -188
- package/src/component/live.ts +13 -7
- package/src/component/localTime.ts +167 -0
- package/src/component/reports.ts +1872 -197
- package/src/component/retention.ts +788 -96
- package/src/component/rollupStore.ts +799 -0
- package/src/component/rollups.ts +82 -0
- package/src/component/sanitize.ts +144 -29
- package/src/component/schema.ts +217 -21
- package/src/component/sites.ts +59 -12
- package/src/component/useragent.ts +171 -0
- package/src/component/validators.ts +49 -14
- package/src/component/visitors.ts +116 -0
- package/src/component/vitals.ts +146 -0
|
@@ -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",
|
|
@@ -172,6 +185,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
172
185
|
latitude?: number;
|
|
173
186
|
longitude?: number;
|
|
174
187
|
os?: string;
|
|
188
|
+
visitorKey?: string;
|
|
175
189
|
};
|
|
176
190
|
events: Array<{
|
|
177
191
|
affiliateSlug?: string;
|
|
@@ -193,7 +207,10 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
193
207
|
| "custom"
|
|
194
208
|
| "conversion"
|
|
195
209
|
| "heartbeat"
|
|
196
|
-
| "
|
|
210
|
+
| "leave"
|
|
211
|
+
| "outbound"
|
|
212
|
+
| "vital";
|
|
213
|
+
value?: number;
|
|
197
214
|
visitorId: string;
|
|
198
215
|
}>;
|
|
199
216
|
origin: string;
|
|
@@ -244,10 +261,62 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
244
261
|
updatedAt: number;
|
|
245
262
|
visits: number;
|
|
246
263
|
}>;
|
|
264
|
+
metadata: {
|
|
265
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
266
|
+
coverage: {
|
|
267
|
+
availableFrom: number | null;
|
|
268
|
+
availableThrough: number | null;
|
|
269
|
+
generation: number | null;
|
|
270
|
+
retainedBefore: number | null;
|
|
271
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
272
|
+
};
|
|
273
|
+
range: {
|
|
274
|
+
boundary: "inclusive";
|
|
275
|
+
from: number;
|
|
276
|
+
timezone: string;
|
|
277
|
+
to: number;
|
|
278
|
+
};
|
|
279
|
+
uniqueVisitors: {
|
|
280
|
+
basis: "estimatedUnique" | "exactLegacy";
|
|
281
|
+
exact: boolean;
|
|
282
|
+
};
|
|
283
|
+
};
|
|
247
284
|
truncated: boolean;
|
|
248
285
|
},
|
|
249
286
|
Name
|
|
250
287
|
>;
|
|
288
|
+
dataCoverage: FunctionReference<
|
|
289
|
+
"query",
|
|
290
|
+
"internal",
|
|
291
|
+
{ from: number; siteId: string; to: number },
|
|
292
|
+
{
|
|
293
|
+
datasets: Array<{
|
|
294
|
+
coverage: {
|
|
295
|
+
availableFrom: number | null;
|
|
296
|
+
availableThrough: number | null;
|
|
297
|
+
generation: number | null;
|
|
298
|
+
retainedBefore: number | null;
|
|
299
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
300
|
+
};
|
|
301
|
+
dataset:
|
|
302
|
+
| "overviewHour"
|
|
303
|
+
| "overviewDay"
|
|
304
|
+
| "overviewLocalDay"
|
|
305
|
+
| "events"
|
|
306
|
+
| "sessions"
|
|
307
|
+
| "conversions"
|
|
308
|
+
| "goals"
|
|
309
|
+
| "funnels"
|
|
310
|
+
| "affiliates"
|
|
311
|
+
| "vitals"
|
|
312
|
+
| "siteMap";
|
|
313
|
+
}>;
|
|
314
|
+
from: number;
|
|
315
|
+
siteId: string;
|
|
316
|
+
to: number;
|
|
317
|
+
},
|
|
318
|
+
Name
|
|
319
|
+
>;
|
|
251
320
|
funnelsReport: FunctionReference<
|
|
252
321
|
"query",
|
|
253
322
|
"internal",
|
|
@@ -259,6 +328,22 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
259
328
|
conversionWindowMs: number;
|
|
260
329
|
createdAt: number;
|
|
261
330
|
key: string;
|
|
331
|
+
metadata: {
|
|
332
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
333
|
+
coverage: {
|
|
334
|
+
availableFrom: number | null;
|
|
335
|
+
availableThrough: number | null;
|
|
336
|
+
generation: number | null;
|
|
337
|
+
retainedBefore: number | null;
|
|
338
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
339
|
+
};
|
|
340
|
+
range: {
|
|
341
|
+
boundary: "inclusive";
|
|
342
|
+
from: number;
|
|
343
|
+
timezone: string;
|
|
344
|
+
to: number;
|
|
345
|
+
};
|
|
346
|
+
};
|
|
262
347
|
name: string;
|
|
263
348
|
siteId: string;
|
|
264
349
|
steps: Array<{
|
|
@@ -296,6 +381,22 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
296
381
|
updatedAt: number;
|
|
297
382
|
valueCents: number;
|
|
298
383
|
}>;
|
|
384
|
+
metadata: {
|
|
385
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
386
|
+
coverage: {
|
|
387
|
+
availableFrom: number | null;
|
|
388
|
+
availableThrough: number | null;
|
|
389
|
+
generation: number | null;
|
|
390
|
+
retainedBefore: number | null;
|
|
391
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
392
|
+
};
|
|
393
|
+
range: {
|
|
394
|
+
boundary: "inclusive";
|
|
395
|
+
from: number;
|
|
396
|
+
timezone: string;
|
|
397
|
+
to: number;
|
|
398
|
+
};
|
|
399
|
+
};
|
|
299
400
|
truncated: boolean;
|
|
300
401
|
},
|
|
301
402
|
Name
|
|
@@ -394,6 +495,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
394
495
|
utmMedium?: string;
|
|
395
496
|
utmSource?: string;
|
|
396
497
|
visitorId: string;
|
|
498
|
+
visitorKey?: string;
|
|
397
499
|
}>;
|
|
398
500
|
pageStatus?: "SplitRecommended" | "SplitRequired" | null;
|
|
399
501
|
splitCursor?: string | null;
|
|
@@ -416,9 +518,11 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
416
518
|
lastSeenAt: number;
|
|
417
519
|
latitude?: number;
|
|
418
520
|
longitude?: number;
|
|
521
|
+
movedAt?: number;
|
|
419
522
|
os: string;
|
|
420
523
|
pageviewCount: number;
|
|
421
524
|
path: string;
|
|
525
|
+
previousPath?: string;
|
|
422
526
|
sessionId: string;
|
|
423
527
|
siteId: string;
|
|
424
528
|
source: string;
|
|
@@ -440,11 +544,53 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
440
544
|
currency: string;
|
|
441
545
|
from: number;
|
|
442
546
|
interval: "hour" | "day";
|
|
547
|
+
metadata: {
|
|
548
|
+
breakdowns: {
|
|
549
|
+
topBrowsers: "pageviews" | "sessions" | "eventVolume";
|
|
550
|
+
topCampaigns: "pageviews" | "sessions" | "eventVolume";
|
|
551
|
+
topCountries: "pageviews" | "sessions" | "eventVolume";
|
|
552
|
+
topDevices: "pageviews" | "sessions" | "eventVolume";
|
|
553
|
+
topEvents: "eventVolume";
|
|
554
|
+
topGoals: "goalCompletionVolume";
|
|
555
|
+
topPages: "pageviews" | "sessions" | "eventVolume";
|
|
556
|
+
topSources: "pageviews" | "sessions" | "eventVolume";
|
|
557
|
+
};
|
|
558
|
+
coverage: {
|
|
559
|
+
availableFrom: number | null;
|
|
560
|
+
availableThrough: number | null;
|
|
561
|
+
generation: number | null;
|
|
562
|
+
retainedBefore: number | null;
|
|
563
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
564
|
+
};
|
|
565
|
+
engagement: "counted" | "unavailable";
|
|
566
|
+
features: {
|
|
567
|
+
botDetection: "userAgent";
|
|
568
|
+
errorInsights: "unsupported";
|
|
569
|
+
outboundLinks: "eventVolume";
|
|
570
|
+
sessionReplay: "unsupported";
|
|
571
|
+
};
|
|
572
|
+
range: {
|
|
573
|
+
boundary: "inclusive";
|
|
574
|
+
from: number;
|
|
575
|
+
timezone: string;
|
|
576
|
+
to: number;
|
|
577
|
+
};
|
|
578
|
+
visitors: {
|
|
579
|
+
basis: "estimatedUnique" | "bucketOccurrences";
|
|
580
|
+
exact: boolean;
|
|
581
|
+
legacyIncomplete: boolean;
|
|
582
|
+
precision: number | null;
|
|
583
|
+
sketchVersion: number | null;
|
|
584
|
+
};
|
|
585
|
+
};
|
|
443
586
|
timeSeries: Array<{
|
|
444
587
|
clicks: number;
|
|
588
|
+
continuedSessions: number;
|
|
445
589
|
conversions: number;
|
|
446
590
|
customEvents: number;
|
|
591
|
+
durationMs: number;
|
|
447
592
|
events: number;
|
|
593
|
+
newVisitors: number;
|
|
448
594
|
outboundClicks: number;
|
|
449
595
|
pageviews: number;
|
|
450
596
|
revenueCents: number;
|
|
@@ -458,6 +604,11 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
458
604
|
revenueCents: number;
|
|
459
605
|
value: string;
|
|
460
606
|
}>;
|
|
607
|
+
topCampaigns: Array<{
|
|
608
|
+
count: number;
|
|
609
|
+
revenueCents: number;
|
|
610
|
+
value: string;
|
|
611
|
+
}>;
|
|
461
612
|
topCountries: Array<{
|
|
462
613
|
count: number;
|
|
463
614
|
revenueCents: number;
|
|
@@ -490,9 +641,12 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
490
641
|
}>;
|
|
491
642
|
totals: {
|
|
492
643
|
clicks: number;
|
|
644
|
+
continuedSessions: number;
|
|
493
645
|
conversions: number;
|
|
494
646
|
customEvents: number;
|
|
647
|
+
durationMs: number;
|
|
495
648
|
events: number;
|
|
649
|
+
newVisitors: number;
|
|
496
650
|
outboundClicks: number;
|
|
497
651
|
pageviews: number;
|
|
498
652
|
revenueCents: number;
|
|
@@ -552,7 +706,9 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
552
706
|
| "custom"
|
|
553
707
|
| "conversion"
|
|
554
708
|
| "heartbeat"
|
|
555
|
-
| "
|
|
709
|
+
| "leave"
|
|
710
|
+
| "outbound"
|
|
711
|
+
| "vital";
|
|
556
712
|
visitorId: string;
|
|
557
713
|
}>;
|
|
558
714
|
pageStatus?: "SplitRecommended" | "SplitRequired" | null;
|
|
@@ -560,6 +716,54 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
560
716
|
},
|
|
561
717
|
Name
|
|
562
718
|
>;
|
|
719
|
+
siteMap: FunctionReference<
|
|
720
|
+
"query",
|
|
721
|
+
"internal",
|
|
722
|
+
{ from: number; siteIds: Array<string>; to: number },
|
|
723
|
+
{
|
|
724
|
+
from: number;
|
|
725
|
+
metadata: {
|
|
726
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
727
|
+
coverage: {
|
|
728
|
+
availableFrom: number | null;
|
|
729
|
+
availableThrough: number | null;
|
|
730
|
+
generation: number | null;
|
|
731
|
+
retainedBefore: number | null;
|
|
732
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
733
|
+
};
|
|
734
|
+
limits: {
|
|
735
|
+
routesPerDay: number;
|
|
736
|
+
rows: number;
|
|
737
|
+
transitionSlots: number;
|
|
738
|
+
};
|
|
739
|
+
range: {
|
|
740
|
+
boundary: "inclusive";
|
|
741
|
+
from: number;
|
|
742
|
+
timezone: string;
|
|
743
|
+
to: number;
|
|
744
|
+
};
|
|
745
|
+
visitors: { basis: "estimatedUnique"; exact: boolean };
|
|
746
|
+
};
|
|
747
|
+
routes: Array<{
|
|
748
|
+
bounces: number;
|
|
749
|
+
daily: Array<{ bucketStart: number; pageviews: number }>;
|
|
750
|
+
dwellMs: number;
|
|
751
|
+
dwellSamples: number;
|
|
752
|
+
entries: number;
|
|
753
|
+
exits: number;
|
|
754
|
+
pageviews: number;
|
|
755
|
+
route: string;
|
|
756
|
+
transitionsIn: number;
|
|
757
|
+
transitionsOut: number;
|
|
758
|
+
visitors: number;
|
|
759
|
+
}>;
|
|
760
|
+
to: number;
|
|
761
|
+
transitions: Array<{ count: number; from: string; to: string }>;
|
|
762
|
+
truncated: { routes: boolean; transitions: boolean };
|
|
763
|
+
window: { from: number; to: number } | null;
|
|
764
|
+
},
|
|
765
|
+
Name
|
|
766
|
+
>;
|
|
563
767
|
visitorJourney: FunctionReference<
|
|
564
768
|
"query",
|
|
565
769
|
"internal",
|
|
@@ -600,11 +804,70 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
600
804
|
| "custom"
|
|
601
805
|
| "conversion"
|
|
602
806
|
| "heartbeat"
|
|
603
|
-
| "
|
|
807
|
+
| "leave"
|
|
808
|
+
| "outbound"
|
|
809
|
+
| "vital";
|
|
604
810
|
visitorId: string;
|
|
605
811
|
}>,
|
|
606
812
|
Name
|
|
607
813
|
>;
|
|
814
|
+
vitalsReport: FunctionReference<
|
|
815
|
+
"query",
|
|
816
|
+
"internal",
|
|
817
|
+
{ from: number; siteId: string; to: number },
|
|
818
|
+
{
|
|
819
|
+
metadata: {
|
|
820
|
+
basis: "dailyRollup" | "boundedLegacy";
|
|
821
|
+
coverage: {
|
|
822
|
+
availableFrom: number | null;
|
|
823
|
+
availableThrough: number | null;
|
|
824
|
+
generation: number | null;
|
|
825
|
+
retainedBefore: number | null;
|
|
826
|
+
state: "complete" | "partial" | "retained" | "unavailable";
|
|
827
|
+
};
|
|
828
|
+
range: {
|
|
829
|
+
boundary: "inclusive";
|
|
830
|
+
from: number;
|
|
831
|
+
timezone: string;
|
|
832
|
+
to: number;
|
|
833
|
+
};
|
|
834
|
+
};
|
|
835
|
+
metrics: Array<{
|
|
836
|
+
average: number;
|
|
837
|
+
count: number;
|
|
838
|
+
devices: Array<{
|
|
839
|
+
average: number;
|
|
840
|
+
count: number;
|
|
841
|
+
device: string;
|
|
842
|
+
good: number;
|
|
843
|
+
needsImprovement: number;
|
|
844
|
+
p75: number;
|
|
845
|
+
poor: number;
|
|
846
|
+
}>;
|
|
847
|
+
good: number;
|
|
848
|
+
metric: "LCP" | "CLS" | "INP" | "FCP" | "TTFB";
|
|
849
|
+
needsImprovement: number;
|
|
850
|
+
p75: number;
|
|
851
|
+
pages: Array<{
|
|
852
|
+
average: number;
|
|
853
|
+
count: number;
|
|
854
|
+
good: number;
|
|
855
|
+
needsImprovement: number;
|
|
856
|
+
p75: number;
|
|
857
|
+
page: string;
|
|
858
|
+
poor: number;
|
|
859
|
+
}>;
|
|
860
|
+
poor: number;
|
|
861
|
+
timeSeries: Array<{
|
|
862
|
+
count: number;
|
|
863
|
+
p75: number;
|
|
864
|
+
timestamp: number;
|
|
865
|
+
}>;
|
|
866
|
+
}>;
|
|
867
|
+
truncated: boolean;
|
|
868
|
+
},
|
|
869
|
+
Name
|
|
870
|
+
>;
|
|
608
871
|
};
|
|
609
872
|
retention: {
|
|
610
873
|
cleanup: FunctionReference<
|
|
@@ -623,13 +886,111 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
623
886
|
| "funnelProgress"
|
|
624
887
|
| "funnelSteps"
|
|
625
888
|
| "affiliateStats"
|
|
889
|
+
| "goalRollups"
|
|
890
|
+
| "funnelRollups"
|
|
891
|
+
| "affiliateRollups"
|
|
892
|
+
| "vitalRollups"
|
|
893
|
+
| "siteMapRollups"
|
|
626
894
|
| "attributions"
|
|
627
895
|
| "liveSessions"
|
|
628
896
|
| "ingestWindows"
|
|
629
897
|
| "siteIngestWindows";
|
|
630
898
|
siteId: string;
|
|
631
899
|
},
|
|
632
|
-
{ deleted: number; hasMore: boolean },
|
|
900
|
+
{ before: number; deleted: number; hasMore: boolean },
|
|
901
|
+
Name
|
|
902
|
+
>;
|
|
903
|
+
disablePolicy: FunctionReference<
|
|
904
|
+
"mutation",
|
|
905
|
+
"internal",
|
|
906
|
+
{ siteId: string },
|
|
907
|
+
null,
|
|
908
|
+
Name
|
|
909
|
+
>;
|
|
910
|
+
runPolicy: FunctionReference<
|
|
911
|
+
"mutation",
|
|
912
|
+
"internal",
|
|
913
|
+
{
|
|
914
|
+
batchSize?: number;
|
|
915
|
+
kind:
|
|
916
|
+
| "events"
|
|
917
|
+
| "sessions"
|
|
918
|
+
| "conversions"
|
|
919
|
+
| "goalCompletions"
|
|
920
|
+
| "aggregateHour"
|
|
921
|
+
| "aggregateDay"
|
|
922
|
+
| "funnelProgress"
|
|
923
|
+
| "funnelSteps"
|
|
924
|
+
| "affiliateStats"
|
|
925
|
+
| "goalRollups"
|
|
926
|
+
| "funnelRollups"
|
|
927
|
+
| "affiliateRollups"
|
|
928
|
+
| "vitalRollups"
|
|
929
|
+
| "siteMapRollups"
|
|
930
|
+
| "attributions"
|
|
931
|
+
| "liveSessions"
|
|
932
|
+
| "ingestWindows"
|
|
933
|
+
| "siteIngestWindows";
|
|
934
|
+
siteId: string;
|
|
935
|
+
},
|
|
936
|
+
{ before: number; deleted: number; hasMore: boolean },
|
|
937
|
+
Name
|
|
938
|
+
>;
|
|
939
|
+
setPolicy: FunctionReference<
|
|
940
|
+
"mutation",
|
|
941
|
+
"internal",
|
|
942
|
+
{
|
|
943
|
+
aggregatesDays: number;
|
|
944
|
+
conversionsDays: number;
|
|
945
|
+
eventsDays: number;
|
|
946
|
+
reportsDays: number;
|
|
947
|
+
sessionsDays: number;
|
|
948
|
+
siteId: string;
|
|
949
|
+
},
|
|
950
|
+
null,
|
|
951
|
+
Name
|
|
952
|
+
>;
|
|
953
|
+
status: FunctionReference<
|
|
954
|
+
"query",
|
|
955
|
+
"internal",
|
|
956
|
+
{ siteId: string },
|
|
957
|
+
{
|
|
958
|
+
backlog: number;
|
|
959
|
+
jobs: Array<{
|
|
960
|
+
before: number;
|
|
961
|
+
deleted: number;
|
|
962
|
+
kind:
|
|
963
|
+
| "events"
|
|
964
|
+
| "sessions"
|
|
965
|
+
| "conversions"
|
|
966
|
+
| "goalCompletions"
|
|
967
|
+
| "aggregateHour"
|
|
968
|
+
| "aggregateDay"
|
|
969
|
+
| "funnelProgress"
|
|
970
|
+
| "funnelSteps"
|
|
971
|
+
| "affiliateStats"
|
|
972
|
+
| "goalRollups"
|
|
973
|
+
| "funnelRollups"
|
|
974
|
+
| "affiliateRollups"
|
|
975
|
+
| "vitalRollups"
|
|
976
|
+
| "siteMapRollups"
|
|
977
|
+
| "attributions"
|
|
978
|
+
| "liveSessions"
|
|
979
|
+
| "ingestWindows"
|
|
980
|
+
| "siteIngestWindows";
|
|
981
|
+
scheduledAt: number | null;
|
|
982
|
+
status: "pending" | "running" | "complete";
|
|
983
|
+
updatedAt: number;
|
|
984
|
+
}>;
|
|
985
|
+
policy: {
|
|
986
|
+
aggregatesDays: number;
|
|
987
|
+
conversionsDays: number;
|
|
988
|
+
eventsDays: number;
|
|
989
|
+
reportsDays: number;
|
|
990
|
+
sessionsDays: number;
|
|
991
|
+
updatedAt: number;
|
|
992
|
+
} | null;
|
|
993
|
+
},
|
|
633
994
|
Name
|
|
634
995
|
>;
|
|
635
996
|
};
|
|
@@ -687,6 +1048,13 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
687
1048
|
}>,
|
|
688
1049
|
Name
|
|
689
1050
|
>;
|
|
1051
|
+
localDays: FunctionReference<
|
|
1052
|
+
"query",
|
|
1053
|
+
"internal",
|
|
1054
|
+
{ siteId: string },
|
|
1055
|
+
{ since: number; timezone: string } | null,
|
|
1056
|
+
Name
|
|
1057
|
+
>;
|
|
690
1058
|
update: FunctionReference<
|
|
691
1059
|
"mutation",
|
|
692
1060
|
"internal",
|
|
@@ -703,4 +1071,13 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
703
1071
|
Name
|
|
704
1072
|
>;
|
|
705
1073
|
};
|
|
1074
|
+
visitors: {
|
|
1075
|
+
link: FunctionReference<
|
|
1076
|
+
"mutation",
|
|
1077
|
+
"internal",
|
|
1078
|
+
{ previousVisitorId: string; siteId: string; visitorId: string },
|
|
1079
|
+
{ aliasCount: number; linked: boolean },
|
|
1080
|
+
Name
|
|
1081
|
+
>;
|
|
1082
|
+
};
|
|
706
1083
|
};
|
|
@@ -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
|
/**
|
|
@@ -22,16 +22,27 @@ export const upsert = mutation({
|
|
|
22
22
|
},
|
|
23
23
|
returns: v.id("affiliates"),
|
|
24
24
|
handler: async (ctx, args) => {
|
|
25
|
-
if (!(await ctx.db.get("sites", args.siteId)))
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if (!(await ctx.db.get("sites", args.siteId)))
|
|
26
|
+
fail("NOT_FOUND", "site not found");
|
|
27
|
+
if (
|
|
28
|
+
!Number.isSafeInteger(args.commissionBps) ||
|
|
29
|
+
args.commissionBps < 0 ||
|
|
30
|
+
args.commissionBps > 10_000
|
|
31
|
+
) {
|
|
32
|
+
fail(
|
|
33
|
+
"INVALID_ARGUMENT",
|
|
34
|
+
"commissionBps must be an integer between 0 and 10000",
|
|
35
|
+
);
|
|
28
36
|
}
|
|
29
37
|
if (
|
|
30
38
|
!Number.isSafeInteger(args.attributionWindowDays) ||
|
|
31
39
|
args.attributionWindowDays < 1 ||
|
|
32
40
|
args.attributionWindowDays > 365
|
|
33
41
|
) {
|
|
34
|
-
fail(
|
|
42
|
+
fail(
|
|
43
|
+
"INVALID_ARGUMENT",
|
|
44
|
+
"attributionWindowDays must be an integer between 1 and 365",
|
|
45
|
+
);
|
|
35
46
|
}
|
|
36
47
|
let slug: string;
|
|
37
48
|
try {
|
|
@@ -52,7 +63,11 @@ export const upsert = mutation({
|
|
|
52
63
|
)
|
|
53
64
|
.unique();
|
|
54
65
|
const affiliateId = args.affiliateId ?? existingBySlug?._id;
|
|
55
|
-
if (
|
|
66
|
+
if (
|
|
67
|
+
args.affiliateId &&
|
|
68
|
+
existingBySlug &&
|
|
69
|
+
existingBySlug._id !== args.affiliateId
|
|
70
|
+
) {
|
|
56
71
|
fail("CONFLICT", "another affiliate already uses this slug");
|
|
57
72
|
}
|
|
58
73
|
const now = Date.now();
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const SKETCH_VERSION = 1 as const;
|
|
2
|
+
const DEFAULT_PRECISION = 6;
|
|
3
|
+
|
|
4
|
+
export type VisitorSketch = {
|
|
5
|
+
version: typeof SKETCH_VERSION;
|
|
6
|
+
precision: number;
|
|
7
|
+
registers: ArrayBuffer;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function createVisitorSketch(
|
|
11
|
+
precision = DEFAULT_PRECISION,
|
|
12
|
+
): VisitorSketch {
|
|
13
|
+
if (!Number.isSafeInteger(precision) || precision < 4 || precision > 10) {
|
|
14
|
+
throw new Error("visitor sketch precision must be between 4 and 10");
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
version: SKETCH_VERSION,
|
|
18
|
+
precision,
|
|
19
|
+
registers: new Uint8Array(1 << precision).buffer,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function addVisitor(
|
|
24
|
+
sketch: VisitorSketch,
|
|
25
|
+
visitorId: string,
|
|
26
|
+
): VisitorSketch {
|
|
27
|
+
const registers = checkedRegisters(sketch).slice();
|
|
28
|
+
const hash = fnv1a64(visitorId);
|
|
29
|
+
const remainingBits = 64 - sketch.precision;
|
|
30
|
+
const index = Number(hash >> BigInt(remainingBits));
|
|
31
|
+
const remaining = hash & ((1n << BigInt(remainingBits)) - 1n);
|
|
32
|
+
const rank = leadingZeroRank(remaining, remainingBits);
|
|
33
|
+
registers[index] = Math.max(registers[index] ?? 0, rank);
|
|
34
|
+
return { ...sketch, registers: registers.buffer };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function unionVisitorSketches(
|
|
38
|
+
left: VisitorSketch,
|
|
39
|
+
right: VisitorSketch,
|
|
40
|
+
): VisitorSketch {
|
|
41
|
+
const leftRegisters = checkedRegisters(left);
|
|
42
|
+
const rightRegisters = checkedRegisters(right);
|
|
43
|
+
if (
|
|
44
|
+
left.version !== right.version ||
|
|
45
|
+
left.precision !== right.precision ||
|
|
46
|
+
leftRegisters.length !== rightRegisters.length
|
|
47
|
+
) {
|
|
48
|
+
throw new Error("visitor sketches are incompatible");
|
|
49
|
+
}
|
|
50
|
+
const registers = leftRegisters.slice();
|
|
51
|
+
for (let index = 0; index < registers.length; index += 1) {
|
|
52
|
+
registers[index] = Math.max(
|
|
53
|
+
registers[index] ?? 0,
|
|
54
|
+
rightRegisters[index] ?? 0,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return { ...left, registers: registers.buffer };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function estimateVisitors(sketch: VisitorSketch): number {
|
|
61
|
+
const registers = checkedRegisters(sketch);
|
|
62
|
+
const count = registers.length;
|
|
63
|
+
const alpha =
|
|
64
|
+
count === 16
|
|
65
|
+
? 0.673
|
|
66
|
+
: count === 32
|
|
67
|
+
? 0.697
|
|
68
|
+
: count === 64
|
|
69
|
+
? 0.709
|
|
70
|
+
: 0.7213 / (1 + 1.079 / count);
|
|
71
|
+
let denominator = 0;
|
|
72
|
+
let empty = 0;
|
|
73
|
+
for (const register of registers) {
|
|
74
|
+
denominator += 2 ** -register;
|
|
75
|
+
if (register === 0) empty += 1;
|
|
76
|
+
}
|
|
77
|
+
let estimate = (alpha * count * count) / denominator;
|
|
78
|
+
if (estimate <= 2.5 * count && empty > 0) {
|
|
79
|
+
estimate = count * Math.log(count / empty);
|
|
80
|
+
}
|
|
81
|
+
return Math.max(0, Math.round(estimate));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function checkedRegisters(sketch: VisitorSketch): Uint8Array<ArrayBuffer> {
|
|
85
|
+
if (sketch.version !== SKETCH_VERSION) {
|
|
86
|
+
throw new Error("unsupported visitor sketch version");
|
|
87
|
+
}
|
|
88
|
+
const registers = new Uint8Array(sketch.registers);
|
|
89
|
+
if (registers.length !== 1 << sketch.precision) {
|
|
90
|
+
throw new Error("visitor sketch has invalid register storage");
|
|
91
|
+
}
|
|
92
|
+
return registers;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function leadingZeroRank(value: bigint, bits: number): number {
|
|
96
|
+
if (value === 0n) return bits + 1;
|
|
97
|
+
let rank = 1;
|
|
98
|
+
for (let bit = bits - 1; bit >= 0; bit -= 1) {
|
|
99
|
+
if ((value & (1n << BigInt(bit))) !== 0n) break;
|
|
100
|
+
rank += 1;
|
|
101
|
+
}
|
|
102
|
+
return rank;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function fnv1a64(value: string): bigint {
|
|
106
|
+
let hash = 0xcbf29ce484222325n;
|
|
107
|
+
for (const byte of new TextEncoder().encode(value)) {
|
|
108
|
+
hash ^= BigInt(byte);
|
|
109
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
110
|
+
}
|
|
111
|
+
hash ^= hash >> 33n;
|
|
112
|
+
hash = BigInt.asUintN(64, hash * 0xff51afd7ed558ccdn);
|
|
113
|
+
hash ^= hash >> 33n;
|
|
114
|
+
hash = BigInt.asUintN(64, hash * 0xc4ceb9fe1a85ec53n);
|
|
115
|
+
hash ^= hash >> 33n;
|
|
116
|
+
return hash;
|
|
117
|
+
}
|