@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
package/dist/client/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { mutationGeneric, paginationOptsValidator, paginationResultValidator, queryGeneric, } from "convex/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
|
-
import { eventPropertiesValidator, eventTypeValidator, funnelStepValidator, goalMatchTypeValidator, ingestContextValidator, ingestResultValidator, trackerEventValidator, } from "../component/validators.js";
|
|
4
|
-
|
|
2
|
+
import { ConvexError, v } from "convex/values";
|
|
3
|
+
import { eventPropertiesValidator, eventTypeValidator, funnelStepValidator, goalMatchTypeValidator, ingestContextValidator, ingestResultValidator, trackerEventValidator, vitalMetricValidator, } from "../component/validators.js";
|
|
4
|
+
import { createFederatedAnalyticsConnectionAuthorizer, createFederatedAnalyticsManifest, FEDERATED_ANALYTICS_CONFIGURE_PERMISSION, FEDERATED_ANALYTICS_CONNECTOR_MANIFEST, FEDERATED_ANALYTICS_READ_PERMISSION, federatedAnalyticsConnectionStatusValidator, federatedAnalyticsManifestValidator, federatedSiteSummaryValidator, } from "./federation.js";
|
|
5
|
+
export { eventPropertiesValidator, eventTypeValidator, funnelStepValidator, goalMatchTypeValidator, ingestContextValidator, ingestResultValidator, trackerEventValidator, vitalMetricValidator, };
|
|
6
|
+
export { VITAL_METRICS, VITAL_THRESHOLDS } from "../component/vitals.js";
|
|
7
|
+
export { normalizeRoute } from "../component/sanitize.js";
|
|
8
|
+
export { localDayStart, nextLocalDayStart, previousLocalDayStart, } from "../component/localTime.js";
|
|
9
|
+
export { effectiveFederatedPermissions, FEDERATED_ANALYTICS_CLAIMS, FEDERATED_ANALYTICS_CONFIGURE_FUNCTIONS, FEDERATED_ANALYTICS_CONFIGURE_PERMISSION, FEDERATED_ANALYTICS_CONNECTOR_MANIFEST, FEDERATED_ANALYTICS_ERROR_CODES, FEDERATED_ANALYTICS_PERMISSIONS, FEDERATED_ANALYTICS_READ_PERMISSION, FEDERATED_ANALYTICS_REQUIRED_CAPABILITIES, FEDERATED_ANALYTICS_REQUIRED_FUNCTIONS, federatedAnalyticsPermissionValidator, } from "./federation.js";
|
|
5
10
|
export class Rastro {
|
|
6
11
|
component;
|
|
7
12
|
constructor(component) {
|
|
@@ -16,6 +21,10 @@ export class Rastro {
|
|
|
16
21
|
getSite(ctx, siteId) {
|
|
17
22
|
return ctx.runQuery(this.component.sites.get, { siteId });
|
|
18
23
|
}
|
|
24
|
+
/** The calendar days a site's daily overview answers in; see `sites.localDays`. */
|
|
25
|
+
localDays(ctx, siteId) {
|
|
26
|
+
return ctx.runQuery(this.component.sites.localDays, { siteId });
|
|
27
|
+
}
|
|
19
28
|
listSites(ctx, args) {
|
|
20
29
|
return ctx.runQuery(this.component.sites.listByOwner, args);
|
|
21
30
|
}
|
|
@@ -25,6 +34,9 @@ export class Rastro {
|
|
|
25
34
|
trackConversion(ctx, args) {
|
|
26
35
|
return ctx.runMutation(this.component.ingest.trackConversion, args);
|
|
27
36
|
}
|
|
37
|
+
linkVisitor(ctx, args) {
|
|
38
|
+
return ctx.runMutation(this.component.visitors.link, args);
|
|
39
|
+
}
|
|
28
40
|
overview(ctx, args) {
|
|
29
41
|
return ctx.runQuery(this.component.reports.overview, args);
|
|
30
42
|
}
|
|
@@ -52,6 +64,12 @@ export class Rastro {
|
|
|
52
64
|
affiliatesReport(ctx, args) {
|
|
53
65
|
return ctx.runQuery(this.component.reports.affiliatesReport, args);
|
|
54
66
|
}
|
|
67
|
+
vitalsReport(ctx, args) {
|
|
68
|
+
return ctx.runQuery(this.component.reports.vitalsReport, args);
|
|
69
|
+
}
|
|
70
|
+
siteMap(ctx, args) {
|
|
71
|
+
return ctx.runQuery(this.component.reports.siteMap, args);
|
|
72
|
+
}
|
|
55
73
|
upsertGoal(ctx, args) {
|
|
56
74
|
return ctx.runMutation(this.component.goals.upsert, args);
|
|
57
75
|
}
|
|
@@ -77,11 +95,29 @@ export class Rastro {
|
|
|
77
95
|
return ctx.runQuery(this.component.affiliates.list, { siteId });
|
|
78
96
|
}
|
|
79
97
|
removeAffiliate(ctx, siteId, affiliateId) {
|
|
80
|
-
return ctx.runMutation(this.component.affiliates.remove, {
|
|
98
|
+
return ctx.runMutation(this.component.affiliates.remove, {
|
|
99
|
+
siteId,
|
|
100
|
+
affiliateId,
|
|
101
|
+
});
|
|
81
102
|
}
|
|
82
103
|
cleanup(ctx, args) {
|
|
83
104
|
return ctx.runMutation(this.component.retention.cleanup, args);
|
|
84
105
|
}
|
|
106
|
+
dataCoverage(ctx, args) {
|
|
107
|
+
return ctx.runQuery(this.component.reports.dataCoverage, args);
|
|
108
|
+
}
|
|
109
|
+
setRetentionPolicy(ctx, args) {
|
|
110
|
+
return ctx.runMutation(this.component.retention.setPolicy, args);
|
|
111
|
+
}
|
|
112
|
+
disableRetentionPolicy(ctx, siteId) {
|
|
113
|
+
return ctx.runMutation(this.component.retention.disablePolicy, { siteId });
|
|
114
|
+
}
|
|
115
|
+
retentionStatus(ctx, siteId) {
|
|
116
|
+
return ctx.runQuery(this.component.retention.status, { siteId });
|
|
117
|
+
}
|
|
118
|
+
runRetentionPolicy(ctx, args) {
|
|
119
|
+
return ctx.runMutation(this.component.retention.runPolicy, args);
|
|
120
|
+
}
|
|
85
121
|
}
|
|
86
122
|
const hostSiteValidator = v.object({
|
|
87
123
|
_id: v.string(),
|
|
@@ -111,6 +147,7 @@ const hostSessionValidator = v.object({
|
|
|
111
147
|
utmSource: v.optional(v.string()),
|
|
112
148
|
utmMedium: v.optional(v.string()),
|
|
113
149
|
utmCampaign: v.optional(v.string()),
|
|
150
|
+
visitorKey: v.optional(v.string()),
|
|
114
151
|
country: v.optional(v.string()),
|
|
115
152
|
city: v.optional(v.string()),
|
|
116
153
|
latitude: v.optional(v.number()),
|
|
@@ -176,6 +213,8 @@ const hostLiveVisitorValidator = v.object({
|
|
|
176
213
|
device: v.string(),
|
|
177
214
|
pageviewCount: v.number(),
|
|
178
215
|
eventCount: v.number(),
|
|
216
|
+
previousPath: v.optional(v.string()),
|
|
217
|
+
movedAt: v.optional(v.number()),
|
|
179
218
|
});
|
|
180
219
|
const hostConversionValidator = v.object({
|
|
181
220
|
_id: v.string(),
|
|
@@ -206,12 +245,67 @@ const hostMetricTotalsValidator = v.object({
|
|
|
206
245
|
visitors: v.number(),
|
|
207
246
|
conversions: v.number(),
|
|
208
247
|
revenueCents: v.number(),
|
|
248
|
+
durationMs: v.number(),
|
|
249
|
+
continuedSessions: v.number(),
|
|
250
|
+
newVisitors: v.number(),
|
|
209
251
|
});
|
|
210
252
|
const hostTopItemValidator = v.object({
|
|
211
253
|
value: v.string(),
|
|
212
254
|
count: v.number(),
|
|
213
255
|
revenueCents: v.number(),
|
|
214
256
|
});
|
|
257
|
+
const hostCoverageValidator = v.object({
|
|
258
|
+
state: v.union(v.literal("complete"), v.literal("partial"), v.literal("retained"), v.literal("unavailable")),
|
|
259
|
+
generation: v.union(v.number(), v.null()),
|
|
260
|
+
availableFrom: v.union(v.number(), v.null()),
|
|
261
|
+
availableThrough: v.union(v.number(), v.null()),
|
|
262
|
+
retainedBefore: v.union(v.number(), v.null()),
|
|
263
|
+
});
|
|
264
|
+
const hostRangeSemanticsValidator = v.object({
|
|
265
|
+
from: v.number(),
|
|
266
|
+
to: v.number(),
|
|
267
|
+
boundary: v.literal("inclusive"),
|
|
268
|
+
// "UTC", or the site timezone whose calendar days a daily overview used.
|
|
269
|
+
timezone: v.string(),
|
|
270
|
+
});
|
|
271
|
+
const hostReportMetadataValidator = v.object({
|
|
272
|
+
basis: v.union(v.literal("dailyRollup"), v.literal("boundedLegacy")),
|
|
273
|
+
coverage: hostCoverageValidator,
|
|
274
|
+
range: hostRangeSemanticsValidator,
|
|
275
|
+
});
|
|
276
|
+
const hostDataCoverageValidator = v.object({
|
|
277
|
+
siteId: v.string(),
|
|
278
|
+
from: v.number(),
|
|
279
|
+
to: v.number(),
|
|
280
|
+
datasets: v.array(v.object({
|
|
281
|
+
dataset: v.union(v.literal("overviewHour"), v.literal("overviewDay"), v.literal("overviewLocalDay"), v.literal("events"), v.literal("sessions"), v.literal("conversions"), v.literal("goals"), v.literal("funnels"), v.literal("affiliates"), v.literal("vitals"), v.literal("siteMap")),
|
|
282
|
+
coverage: hostCoverageValidator,
|
|
283
|
+
})),
|
|
284
|
+
});
|
|
285
|
+
const retentionKindValidator = v.union(v.literal("events"), v.literal("sessions"), v.literal("conversions"), v.literal("goalCompletions"), v.literal("aggregateHour"), v.literal("aggregateDay"), v.literal("funnelProgress"), v.literal("funnelSteps"), v.literal("affiliateStats"), v.literal("goalRollups"), v.literal("funnelRollups"), v.literal("affiliateRollups"), v.literal("vitalRollups"), v.literal("siteMapRollups"), v.literal("attributions"), v.literal("liveSessions"), v.literal("ingestWindows"), v.literal("siteIngestWindows"));
|
|
286
|
+
const hostRetentionPolicyValidator = v.object({
|
|
287
|
+
eventsDays: v.number(),
|
|
288
|
+
sessionsDays: v.number(),
|
|
289
|
+
conversionsDays: v.number(),
|
|
290
|
+
aggregatesDays: v.number(),
|
|
291
|
+
reportsDays: v.number(),
|
|
292
|
+
});
|
|
293
|
+
const hostRetentionStatusValidator = v.object({
|
|
294
|
+
policy: v.union(hostRetentionPolicyValidator.extend({ updatedAt: v.number() }), v.null()),
|
|
295
|
+
jobs: v.array(v.object({
|
|
296
|
+
kind: v.string(),
|
|
297
|
+
before: v.number(),
|
|
298
|
+
status: v.union(v.literal("pending"), v.literal("running"), v.literal("complete")),
|
|
299
|
+
deleted: v.number(),
|
|
300
|
+
scheduledAt: v.union(v.number(), v.null()),
|
|
301
|
+
updatedAt: v.number(),
|
|
302
|
+
})),
|
|
303
|
+
backlog: v.number(),
|
|
304
|
+
});
|
|
305
|
+
// See the component's breakdownBasisValidator: a breakdown's `count` is
|
|
306
|
+
// pageviews (pages) or sessions (every other dimension) once the range is
|
|
307
|
+
// fully covered by those counters, and raw event volume before that.
|
|
308
|
+
const hostBreakdownBasisValidator = v.union(v.literal("pageviews"), v.literal("sessions"), v.literal("eventVolume"));
|
|
215
309
|
const hostOverviewValidator = v.object({
|
|
216
310
|
from: v.number(),
|
|
217
311
|
to: v.number(),
|
|
@@ -220,12 +314,41 @@ const hostOverviewValidator = v.object({
|
|
|
220
314
|
totals: hostMetricTotalsValidator,
|
|
221
315
|
timeSeries: v.array(hostMetricTotalsValidator.extend({ timestamp: v.number() })),
|
|
222
316
|
topSources: v.array(hostTopItemValidator),
|
|
317
|
+
topCampaigns: v.array(hostTopItemValidator),
|
|
223
318
|
topPages: v.array(hostTopItemValidator),
|
|
224
319
|
topCountries: v.array(hostTopItemValidator),
|
|
225
320
|
topDevices: v.array(hostTopItemValidator),
|
|
226
321
|
topBrowsers: v.array(hostTopItemValidator),
|
|
227
322
|
topEvents: v.array(hostTopItemValidator),
|
|
228
323
|
topGoals: v.array(hostTopItemValidator),
|
|
324
|
+
metadata: v.object({
|
|
325
|
+
visitors: v.object({
|
|
326
|
+
basis: v.union(v.literal("estimatedUnique"), v.literal("bucketOccurrences")),
|
|
327
|
+
exact: v.boolean(),
|
|
328
|
+
legacyIncomplete: v.boolean(),
|
|
329
|
+
sketchVersion: v.union(v.number(), v.null()),
|
|
330
|
+
precision: v.union(v.number(), v.null()),
|
|
331
|
+
}),
|
|
332
|
+
breakdowns: v.object({
|
|
333
|
+
topSources: hostBreakdownBasisValidator,
|
|
334
|
+
topCampaigns: hostBreakdownBasisValidator,
|
|
335
|
+
topPages: hostBreakdownBasisValidator,
|
|
336
|
+
topCountries: hostBreakdownBasisValidator,
|
|
337
|
+
topDevices: hostBreakdownBasisValidator,
|
|
338
|
+
topBrowsers: hostBreakdownBasisValidator,
|
|
339
|
+
topEvents: v.literal("eventVolume"),
|
|
340
|
+
topGoals: v.literal("goalCompletionVolume"),
|
|
341
|
+
}),
|
|
342
|
+
engagement: v.union(v.literal("counted"), v.literal("unavailable")),
|
|
343
|
+
coverage: hostCoverageValidator,
|
|
344
|
+
range: hostRangeSemanticsValidator,
|
|
345
|
+
features: v.object({
|
|
346
|
+
sessionReplay: v.literal("unsupported"),
|
|
347
|
+
errorInsights: v.literal("unsupported"),
|
|
348
|
+
outboundLinks: v.literal("eventVolume"),
|
|
349
|
+
botDetection: v.literal("userAgent"),
|
|
350
|
+
}),
|
|
351
|
+
}),
|
|
229
352
|
});
|
|
230
353
|
const hostGoalValidator = v.object({
|
|
231
354
|
_id: v.string(),
|
|
@@ -265,12 +388,22 @@ const hostAffiliateValidator = v.object({
|
|
|
265
388
|
updatedAt: v.number(),
|
|
266
389
|
});
|
|
267
390
|
const hostGoalsReportValidator = v.object({
|
|
268
|
-
goals: v.array(hostGoalValidator.extend({
|
|
391
|
+
goals: v.array(hostGoalValidator.extend({
|
|
392
|
+
completions: v.number(),
|
|
393
|
+
valueCents: v.number(),
|
|
394
|
+
})),
|
|
269
395
|
truncated: v.boolean(),
|
|
396
|
+
metadata: hostReportMetadataValidator,
|
|
270
397
|
});
|
|
271
398
|
const hostFunnelsReportValidator = v.array(hostFunnelValidator.extend({
|
|
272
|
-
stepsReport: v.array(v.object({
|
|
399
|
+
stepsReport: v.array(v.object({
|
|
400
|
+
step: v.number(),
|
|
401
|
+
name: v.string(),
|
|
402
|
+
entrants: v.number(),
|
|
403
|
+
completions: v.number(),
|
|
404
|
+
})),
|
|
273
405
|
truncated: v.boolean(),
|
|
406
|
+
metadata: hostReportMetadataValidator,
|
|
274
407
|
}));
|
|
275
408
|
const hostAffiliatesReportValidator = v.object({
|
|
276
409
|
affiliates: v.array(hostAffiliateValidator.extend({
|
|
@@ -281,6 +414,65 @@ const hostAffiliatesReportValidator = v.object({
|
|
|
281
414
|
commissionCents: v.number(),
|
|
282
415
|
})),
|
|
283
416
|
truncated: v.boolean(),
|
|
417
|
+
metadata: hostReportMetadataValidator.extend({
|
|
418
|
+
uniqueVisitors: v.object({
|
|
419
|
+
basis: v.union(v.literal("estimatedUnique"), v.literal("exactLegacy")),
|
|
420
|
+
exact: v.boolean(),
|
|
421
|
+
}),
|
|
422
|
+
}),
|
|
423
|
+
});
|
|
424
|
+
const hostVitalStatsValidator = v.object({
|
|
425
|
+
count: v.number(),
|
|
426
|
+
p75: v.number(),
|
|
427
|
+
average: v.number(),
|
|
428
|
+
good: v.number(),
|
|
429
|
+
needsImprovement: v.number(),
|
|
430
|
+
poor: v.number(),
|
|
431
|
+
});
|
|
432
|
+
const hostVitalsReportValidator = v.object({
|
|
433
|
+
metrics: v.array(hostVitalStatsValidator.extend({
|
|
434
|
+
metric: vitalMetricValidator,
|
|
435
|
+
devices: v.array(hostVitalStatsValidator.extend({ device: v.string() })),
|
|
436
|
+
pages: v.array(hostVitalStatsValidator.extend({ page: v.string() })),
|
|
437
|
+
timeSeries: v.array(v.object({
|
|
438
|
+
timestamp: v.number(),
|
|
439
|
+
count: v.number(),
|
|
440
|
+
p75: v.number(),
|
|
441
|
+
})),
|
|
442
|
+
})),
|
|
443
|
+
truncated: v.boolean(),
|
|
444
|
+
metadata: hostReportMetadataValidator,
|
|
445
|
+
});
|
|
446
|
+
const hostSiteMapValidator = v.object({
|
|
447
|
+
from: v.number(),
|
|
448
|
+
to: v.number(),
|
|
449
|
+
window: v.union(v.object({ from: v.number(), to: v.number() }), v.null()),
|
|
450
|
+
routes: v.array(v.object({
|
|
451
|
+
route: v.string(),
|
|
452
|
+
pageviews: v.number(),
|
|
453
|
+
visitors: v.number(),
|
|
454
|
+
entries: v.number(),
|
|
455
|
+
exits: v.number(),
|
|
456
|
+
bounces: v.number(),
|
|
457
|
+
transitionsIn: v.number(),
|
|
458
|
+
transitionsOut: v.number(),
|
|
459
|
+
dwellMs: v.number(),
|
|
460
|
+
dwellSamples: v.number(),
|
|
461
|
+
daily: v.array(v.object({ bucketStart: v.number(), pageviews: v.number() })),
|
|
462
|
+
})),
|
|
463
|
+
transitions: v.array(v.object({ from: v.string(), to: v.string(), count: v.number() })),
|
|
464
|
+
truncated: v.object({ routes: v.boolean(), transitions: v.boolean() }),
|
|
465
|
+
metadata: hostReportMetadataValidator.extend({
|
|
466
|
+
visitors: v.object({
|
|
467
|
+
basis: v.literal("estimatedUnique"),
|
|
468
|
+
exact: v.boolean(),
|
|
469
|
+
}),
|
|
470
|
+
limits: v.object({
|
|
471
|
+
routesPerDay: v.number(),
|
|
472
|
+
transitionSlots: v.number(),
|
|
473
|
+
rows: v.number(),
|
|
474
|
+
}),
|
|
475
|
+
}),
|
|
284
476
|
});
|
|
285
477
|
export function exposeAnalyticsApi(component, options) {
|
|
286
478
|
const rastro = new Rastro(component);
|
|
@@ -297,7 +489,11 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
297
489
|
},
|
|
298
490
|
returns: v.string(),
|
|
299
491
|
handler: async (ctx, args) => {
|
|
300
|
-
await options.authorize(ctx, {
|
|
492
|
+
await options.authorize(ctx, {
|
|
493
|
+
operation: "site.create",
|
|
494
|
+
siteIds: [],
|
|
495
|
+
ownerId: args.ownerId,
|
|
496
|
+
});
|
|
301
497
|
return await rastro.createSite(ctx, args);
|
|
302
498
|
},
|
|
303
499
|
}),
|
|
@@ -313,7 +509,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
313
509
|
},
|
|
314
510
|
returns: v.null(),
|
|
315
511
|
handler: async (ctx, args) => {
|
|
316
|
-
await options.authorize(ctx, {
|
|
512
|
+
await options.authorize(ctx, {
|
|
513
|
+
operation: "site.update",
|
|
514
|
+
siteIds: [args.siteId],
|
|
515
|
+
});
|
|
317
516
|
return await rastro.updateSite(ctx, args);
|
|
318
517
|
},
|
|
319
518
|
}),
|
|
@@ -321,7 +520,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
321
520
|
args: { siteId: v.string() },
|
|
322
521
|
returns: v.union(hostSiteValidator, v.null()),
|
|
323
522
|
handler: async (ctx, args) => {
|
|
324
|
-
await options.authorize(ctx, {
|
|
523
|
+
await options.authorize(ctx, {
|
|
524
|
+
operation: "site.get",
|
|
525
|
+
siteIds: [args.siteId],
|
|
526
|
+
});
|
|
325
527
|
return await rastro.getSite(ctx, args.siteId);
|
|
326
528
|
},
|
|
327
529
|
}),
|
|
@@ -329,7 +531,11 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
329
531
|
args: { ownerId: v.string(), limit: v.optional(v.number()) },
|
|
330
532
|
returns: v.array(hostSiteValidator),
|
|
331
533
|
handler: async (ctx, args) => {
|
|
332
|
-
await options.authorize(ctx, {
|
|
534
|
+
await options.authorize(ctx, {
|
|
535
|
+
operation: "site.list",
|
|
536
|
+
siteIds: [],
|
|
537
|
+
ownerId: args.ownerId,
|
|
538
|
+
});
|
|
333
539
|
return await rastro.listSites(ctx, args);
|
|
334
540
|
},
|
|
335
541
|
}),
|
|
@@ -342,7 +548,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
342
548
|
},
|
|
343
549
|
returns: ingestResultValidator,
|
|
344
550
|
handler: async (ctx, args) => {
|
|
345
|
-
await options.authorize(ctx, {
|
|
551
|
+
await options.authorize(ctx, {
|
|
552
|
+
operation: "ingest",
|
|
553
|
+
siteIds: [args.siteId],
|
|
554
|
+
});
|
|
346
555
|
return await rastro.ingest(ctx, args);
|
|
347
556
|
},
|
|
348
557
|
}),
|
|
@@ -361,10 +570,28 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
361
570
|
},
|
|
362
571
|
returns: ingestResultValidator,
|
|
363
572
|
handler: async (ctx, args) => {
|
|
364
|
-
await options.authorize(ctx, {
|
|
573
|
+
await options.authorize(ctx, {
|
|
574
|
+
operation: "conversion.track",
|
|
575
|
+
siteIds: [args.siteId],
|
|
576
|
+
});
|
|
365
577
|
return await rastro.trackConversion(ctx, args);
|
|
366
578
|
},
|
|
367
579
|
}),
|
|
580
|
+
linkVisitor: mutationGeneric({
|
|
581
|
+
args: {
|
|
582
|
+
siteId: v.string(),
|
|
583
|
+
visitorId: v.string(),
|
|
584
|
+
previousVisitorId: v.string(),
|
|
585
|
+
},
|
|
586
|
+
returns: v.object({ linked: v.boolean(), aliasCount: v.number() }),
|
|
587
|
+
handler: async (ctx, args) => {
|
|
588
|
+
await options.authorize(ctx, {
|
|
589
|
+
operation: "visitor.link",
|
|
590
|
+
siteIds: [args.siteId],
|
|
591
|
+
});
|
|
592
|
+
return await rastro.linkVisitor(ctx, args);
|
|
593
|
+
},
|
|
594
|
+
}),
|
|
368
595
|
overview: queryGeneric({
|
|
369
596
|
args: {
|
|
370
597
|
siteIds: v.array(v.string()),
|
|
@@ -374,7 +601,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
374
601
|
},
|
|
375
602
|
returns: hostOverviewValidator,
|
|
376
603
|
handler: async (ctx, args) => {
|
|
377
|
-
await options.authorize(ctx, {
|
|
604
|
+
await options.authorize(ctx, {
|
|
605
|
+
operation: "report.overview",
|
|
606
|
+
siteIds: args.siteIds,
|
|
607
|
+
});
|
|
378
608
|
return await rastro.overview(ctx, args);
|
|
379
609
|
},
|
|
380
610
|
}),
|
|
@@ -386,7 +616,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
386
616
|
},
|
|
387
617
|
returns: v.array(hostLiveVisitorValidator),
|
|
388
618
|
handler: async (ctx, args) => {
|
|
389
|
-
await options.authorize(ctx, {
|
|
619
|
+
await options.authorize(ctx, {
|
|
620
|
+
operation: "report.live",
|
|
621
|
+
siteIds: args.siteIds,
|
|
622
|
+
});
|
|
390
623
|
return await rastro.liveVisitors(ctx, args);
|
|
391
624
|
},
|
|
392
625
|
}),
|
|
@@ -399,7 +632,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
399
632
|
},
|
|
400
633
|
returns: paginationResultValidator(hostSessionValidator),
|
|
401
634
|
handler: async (ctx, args) => {
|
|
402
|
-
await options.authorize(ctx, {
|
|
635
|
+
await options.authorize(ctx, {
|
|
636
|
+
operation: "report.sessions",
|
|
637
|
+
siteIds: [args.siteId],
|
|
638
|
+
});
|
|
403
639
|
return await rastro.listSessions(ctx, args);
|
|
404
640
|
},
|
|
405
641
|
}),
|
|
@@ -413,7 +649,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
413
649
|
},
|
|
414
650
|
returns: paginationResultValidator(hostEventValidator),
|
|
415
651
|
handler: async (ctx, args) => {
|
|
416
|
-
await options.authorize(ctx, {
|
|
652
|
+
await options.authorize(ctx, {
|
|
653
|
+
operation: "report.sessionJourney",
|
|
654
|
+
siteIds: [args.siteId],
|
|
655
|
+
});
|
|
417
656
|
return await rastro.sessionJourney(ctx, args);
|
|
418
657
|
},
|
|
419
658
|
}),
|
|
@@ -426,7 +665,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
426
665
|
},
|
|
427
666
|
returns: paginationResultValidator(hostConversionValidator),
|
|
428
667
|
handler: async (ctx, args) => {
|
|
429
|
-
await options.authorize(ctx, {
|
|
668
|
+
await options.authorize(ctx, {
|
|
669
|
+
operation: "report.conversions",
|
|
670
|
+
siteIds: [args.siteId],
|
|
671
|
+
});
|
|
430
672
|
return await rastro.listConversions(ctx, args);
|
|
431
673
|
},
|
|
432
674
|
}),
|
|
@@ -440,7 +682,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
440
682
|
},
|
|
441
683
|
returns: v.array(hostEventValidator),
|
|
442
684
|
handler: async (ctx, args) => {
|
|
443
|
-
await options.authorize(ctx, {
|
|
685
|
+
await options.authorize(ctx, {
|
|
686
|
+
operation: "report.visitorJourney",
|
|
687
|
+
siteIds: args.siteIds,
|
|
688
|
+
});
|
|
444
689
|
return await rastro.visitorJourney(ctx, args);
|
|
445
690
|
},
|
|
446
691
|
}),
|
|
@@ -448,7 +693,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
448
693
|
args: { siteId: v.string(), from: v.number(), to: v.number() },
|
|
449
694
|
returns: hostGoalsReportValidator,
|
|
450
695
|
handler: async (ctx, args) => {
|
|
451
|
-
await options.authorize(ctx, {
|
|
696
|
+
await options.authorize(ctx, {
|
|
697
|
+
operation: "report.goals",
|
|
698
|
+
siteIds: [args.siteId],
|
|
699
|
+
});
|
|
452
700
|
return await rastro.goalsReport(ctx, args);
|
|
453
701
|
},
|
|
454
702
|
}),
|
|
@@ -456,7 +704,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
456
704
|
args: { siteId: v.string(), from: v.number(), to: v.number() },
|
|
457
705
|
returns: hostFunnelsReportValidator,
|
|
458
706
|
handler: async (ctx, args) => {
|
|
459
|
-
await options.authorize(ctx, {
|
|
707
|
+
await options.authorize(ctx, {
|
|
708
|
+
operation: "report.funnels",
|
|
709
|
+
siteIds: [args.siteId],
|
|
710
|
+
});
|
|
460
711
|
return await rastro.funnelsReport(ctx, args);
|
|
461
712
|
},
|
|
462
713
|
}),
|
|
@@ -464,10 +715,102 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
464
715
|
args: { siteId: v.string(), from: v.number(), to: v.number() },
|
|
465
716
|
returns: hostAffiliatesReportValidator,
|
|
466
717
|
handler: async (ctx, args) => {
|
|
467
|
-
await options.authorize(ctx, {
|
|
718
|
+
await options.authorize(ctx, {
|
|
719
|
+
operation: "report.affiliates",
|
|
720
|
+
siteIds: [args.siteId],
|
|
721
|
+
});
|
|
468
722
|
return await rastro.affiliatesReport(ctx, args);
|
|
469
723
|
},
|
|
470
724
|
}),
|
|
725
|
+
vitalsReport: queryGeneric({
|
|
726
|
+
args: { siteId: v.string(), from: v.number(), to: v.number() },
|
|
727
|
+
returns: hostVitalsReportValidator,
|
|
728
|
+
handler: async (ctx, args) => {
|
|
729
|
+
await options.authorize(ctx, {
|
|
730
|
+
operation: "report.vitals",
|
|
731
|
+
siteIds: [args.siteId],
|
|
732
|
+
});
|
|
733
|
+
return await rastro.vitalsReport(ctx, args);
|
|
734
|
+
},
|
|
735
|
+
}),
|
|
736
|
+
siteMap: queryGeneric({
|
|
737
|
+
args: {
|
|
738
|
+
siteIds: v.array(v.string()),
|
|
739
|
+
from: v.number(),
|
|
740
|
+
to: v.number(),
|
|
741
|
+
},
|
|
742
|
+
returns: hostSiteMapValidator,
|
|
743
|
+
handler: async (ctx, args) => {
|
|
744
|
+
await options.authorize(ctx, {
|
|
745
|
+
operation: "report.siteMap",
|
|
746
|
+
siteIds: args.siteIds,
|
|
747
|
+
});
|
|
748
|
+
return await rastro.siteMap(ctx, args);
|
|
749
|
+
},
|
|
750
|
+
}),
|
|
751
|
+
dataCoverage: queryGeneric({
|
|
752
|
+
args: { siteId: v.string(), from: v.number(), to: v.number() },
|
|
753
|
+
returns: hostDataCoverageValidator,
|
|
754
|
+
handler: async (ctx, args) => {
|
|
755
|
+
await options.authorize(ctx, {
|
|
756
|
+
operation: "coverage.read",
|
|
757
|
+
siteIds: [args.siteId],
|
|
758
|
+
});
|
|
759
|
+
return await rastro.dataCoverage(ctx, args);
|
|
760
|
+
},
|
|
761
|
+
}),
|
|
762
|
+
retentionStatus: queryGeneric({
|
|
763
|
+
args: { siteId: v.string() },
|
|
764
|
+
returns: hostRetentionStatusValidator,
|
|
765
|
+
handler: async (ctx, args) => {
|
|
766
|
+
await options.authorize(ctx, {
|
|
767
|
+
operation: "retention.status",
|
|
768
|
+
siteIds: [args.siteId],
|
|
769
|
+
});
|
|
770
|
+
return await rastro.retentionStatus(ctx, args.siteId);
|
|
771
|
+
},
|
|
772
|
+
}),
|
|
773
|
+
setRetentionPolicy: mutationGeneric({
|
|
774
|
+
args: { siteId: v.string(), ...hostRetentionPolicyValidator.fields },
|
|
775
|
+
returns: v.null(),
|
|
776
|
+
handler: async (ctx, args) => {
|
|
777
|
+
await options.authorize(ctx, {
|
|
778
|
+
operation: "retention.policy",
|
|
779
|
+
siteIds: [args.siteId],
|
|
780
|
+
});
|
|
781
|
+
return await rastro.setRetentionPolicy(ctx, args);
|
|
782
|
+
},
|
|
783
|
+
}),
|
|
784
|
+
disableRetentionPolicy: mutationGeneric({
|
|
785
|
+
args: { siteId: v.string() },
|
|
786
|
+
returns: v.null(),
|
|
787
|
+
handler: async (ctx, args) => {
|
|
788
|
+
await options.authorize(ctx, {
|
|
789
|
+
operation: "retention.policy",
|
|
790
|
+
siteIds: [args.siteId],
|
|
791
|
+
});
|
|
792
|
+
return await rastro.disableRetentionPolicy(ctx, args.siteId);
|
|
793
|
+
},
|
|
794
|
+
}),
|
|
795
|
+
runRetentionPolicy: mutationGeneric({
|
|
796
|
+
args: {
|
|
797
|
+
siteId: v.string(),
|
|
798
|
+
kind: retentionKindValidator,
|
|
799
|
+
batchSize: v.optional(v.number()),
|
|
800
|
+
},
|
|
801
|
+
returns: v.object({
|
|
802
|
+
deleted: v.number(),
|
|
803
|
+
hasMore: v.boolean(),
|
|
804
|
+
before: v.number(),
|
|
805
|
+
}),
|
|
806
|
+
handler: async (ctx, args) => {
|
|
807
|
+
await options.authorize(ctx, {
|
|
808
|
+
operation: "retention.policy",
|
|
809
|
+
siteIds: [args.siteId],
|
|
810
|
+
});
|
|
811
|
+
return await rastro.runRetentionPolicy(ctx, args);
|
|
812
|
+
},
|
|
813
|
+
}),
|
|
471
814
|
upsertGoal: mutationGeneric({
|
|
472
815
|
args: {
|
|
473
816
|
siteId: v.string(),
|
|
@@ -481,7 +824,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
481
824
|
},
|
|
482
825
|
returns: v.string(),
|
|
483
826
|
handler: async (ctx, args) => {
|
|
484
|
-
await options.authorize(ctx, {
|
|
827
|
+
await options.authorize(ctx, {
|
|
828
|
+
operation: "goal.upsert",
|
|
829
|
+
siteIds: [args.siteId],
|
|
830
|
+
});
|
|
485
831
|
return await rastro.upsertGoal(ctx, args);
|
|
486
832
|
},
|
|
487
833
|
}),
|
|
@@ -489,7 +835,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
489
835
|
args: { siteId: v.string() },
|
|
490
836
|
returns: v.array(hostGoalValidator),
|
|
491
837
|
handler: async (ctx, args) => {
|
|
492
|
-
await options.authorize(ctx, {
|
|
838
|
+
await options.authorize(ctx, {
|
|
839
|
+
operation: "goal.list",
|
|
840
|
+
siteIds: [args.siteId],
|
|
841
|
+
});
|
|
493
842
|
return await rastro.listGoals(ctx, args.siteId);
|
|
494
843
|
},
|
|
495
844
|
}),
|
|
@@ -497,7 +846,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
497
846
|
args: { siteId: v.string(), goalId: v.string() },
|
|
498
847
|
returns: v.null(),
|
|
499
848
|
handler: async (ctx, args) => {
|
|
500
|
-
await options.authorize(ctx, {
|
|
849
|
+
await options.authorize(ctx, {
|
|
850
|
+
operation: "goal.remove",
|
|
851
|
+
siteIds: [args.siteId],
|
|
852
|
+
});
|
|
501
853
|
return await rastro.removeGoal(ctx, args.siteId, args.goalId);
|
|
502
854
|
},
|
|
503
855
|
}),
|
|
@@ -513,7 +865,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
513
865
|
},
|
|
514
866
|
returns: v.string(),
|
|
515
867
|
handler: async (ctx, args) => {
|
|
516
|
-
await options.authorize(ctx, {
|
|
868
|
+
await options.authorize(ctx, {
|
|
869
|
+
operation: "funnel.upsert",
|
|
870
|
+
siteIds: [args.siteId],
|
|
871
|
+
});
|
|
517
872
|
return await rastro.upsertFunnel(ctx, args);
|
|
518
873
|
},
|
|
519
874
|
}),
|
|
@@ -521,7 +876,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
521
876
|
args: { siteId: v.string() },
|
|
522
877
|
returns: v.array(hostFunnelValidator),
|
|
523
878
|
handler: async (ctx, args) => {
|
|
524
|
-
await options.authorize(ctx, {
|
|
879
|
+
await options.authorize(ctx, {
|
|
880
|
+
operation: "funnel.list",
|
|
881
|
+
siteIds: [args.siteId],
|
|
882
|
+
});
|
|
525
883
|
return await rastro.listFunnels(ctx, args.siteId);
|
|
526
884
|
},
|
|
527
885
|
}),
|
|
@@ -529,7 +887,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
529
887
|
args: { siteId: v.string(), funnelId: v.string() },
|
|
530
888
|
returns: v.null(),
|
|
531
889
|
handler: async (ctx, args) => {
|
|
532
|
-
await options.authorize(ctx, {
|
|
890
|
+
await options.authorize(ctx, {
|
|
891
|
+
operation: "funnel.remove",
|
|
892
|
+
siteIds: [args.siteId],
|
|
893
|
+
});
|
|
533
894
|
return await rastro.removeFunnel(ctx, args.siteId, args.funnelId);
|
|
534
895
|
},
|
|
535
896
|
}),
|
|
@@ -545,7 +906,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
545
906
|
},
|
|
546
907
|
returns: v.string(),
|
|
547
908
|
handler: async (ctx, args) => {
|
|
548
|
-
await options.authorize(ctx, {
|
|
909
|
+
await options.authorize(ctx, {
|
|
910
|
+
operation: "affiliate.upsert",
|
|
911
|
+
siteIds: [args.siteId],
|
|
912
|
+
});
|
|
549
913
|
return await rastro.upsertAffiliate(ctx, args);
|
|
550
914
|
},
|
|
551
915
|
}),
|
|
@@ -553,7 +917,10 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
553
917
|
args: { siteId: v.string() },
|
|
554
918
|
returns: v.array(hostAffiliateValidator),
|
|
555
919
|
handler: async (ctx, args) => {
|
|
556
|
-
await options.authorize(ctx, {
|
|
920
|
+
await options.authorize(ctx, {
|
|
921
|
+
operation: "affiliate.list",
|
|
922
|
+
siteIds: [args.siteId],
|
|
923
|
+
});
|
|
557
924
|
return await rastro.listAffiliates(ctx, args.siteId);
|
|
558
925
|
},
|
|
559
926
|
}),
|
|
@@ -561,30 +928,195 @@ export function exposeAnalyticsApi(component, options) {
|
|
|
561
928
|
args: { siteId: v.string(), affiliateId: v.string() },
|
|
562
929
|
returns: v.null(),
|
|
563
930
|
handler: async (ctx, args) => {
|
|
564
|
-
await options.authorize(ctx, {
|
|
931
|
+
await options.authorize(ctx, {
|
|
932
|
+
operation: "affiliate.remove",
|
|
933
|
+
siteIds: [args.siteId],
|
|
934
|
+
});
|
|
565
935
|
return await rastro.removeAffiliate(ctx, args.siteId, args.affiliateId);
|
|
566
936
|
},
|
|
567
937
|
}),
|
|
568
938
|
cleanupAnalytics: mutationGeneric({
|
|
569
939
|
args: {
|
|
570
940
|
siteId: v.string(),
|
|
571
|
-
kind: v.union(v.literal("events"), v.literal("sessions"), v.literal("conversions"), v.literal("goalCompletions"), v.literal("aggregateHour"), v.literal("aggregateDay"), v.literal("funnelProgress"), v.literal("funnelSteps"), v.literal("affiliateStats"), v.literal("attributions"), v.literal("liveSessions"), v.literal("ingestWindows"), v.literal("siteIngestWindows")),
|
|
941
|
+
kind: v.union(v.literal("events"), v.literal("sessions"), v.literal("conversions"), v.literal("goalCompletions"), v.literal("aggregateHour"), v.literal("aggregateDay"), v.literal("funnelProgress"), v.literal("funnelSteps"), v.literal("affiliateStats"), v.literal("goalRollups"), v.literal("funnelRollups"), v.literal("affiliateRollups"), v.literal("vitalRollups"), v.literal("attributions"), v.literal("liveSessions"), v.literal("ingestWindows"), v.literal("siteIngestWindows")),
|
|
572
942
|
before: v.number(),
|
|
573
943
|
batchSize: v.optional(v.number()),
|
|
574
944
|
},
|
|
575
|
-
returns: v.object({
|
|
945
|
+
returns: v.object({
|
|
946
|
+
deleted: v.number(),
|
|
947
|
+
hasMore: v.boolean(),
|
|
948
|
+
before: v.number(),
|
|
949
|
+
}),
|
|
576
950
|
handler: async (ctx, args) => {
|
|
577
|
-
await options.authorize(ctx, {
|
|
951
|
+
await options.authorize(ctx, {
|
|
952
|
+
operation: "retention.cleanup",
|
|
953
|
+
siteIds: [args.siteId],
|
|
954
|
+
});
|
|
578
955
|
return await rastro.cleanup(ctx, args);
|
|
579
956
|
},
|
|
580
957
|
}),
|
|
581
958
|
};
|
|
582
959
|
}
|
|
960
|
+
/**
|
|
961
|
+
* The federated operations that change a site, and so run only with the
|
|
962
|
+
* configure permission on both the token and the host's grant. Everything else
|
|
963
|
+
* on the federated surface is a read.
|
|
964
|
+
*/
|
|
965
|
+
const FEDERATED_CONFIGURE_OPERATIONS = new Set([
|
|
966
|
+
"site.update",
|
|
967
|
+
"goal.upsert",
|
|
968
|
+
"goal.list",
|
|
969
|
+
"goal.remove",
|
|
970
|
+
"funnel.upsert",
|
|
971
|
+
"funnel.list",
|
|
972
|
+
"funnel.remove",
|
|
973
|
+
"affiliate.upsert",
|
|
974
|
+
"affiliate.list",
|
|
975
|
+
"affiliate.remove",
|
|
976
|
+
"retention.policy",
|
|
977
|
+
"retention.status",
|
|
978
|
+
]);
|
|
979
|
+
const federatedSiteSettingsValidator = v.object({
|
|
980
|
+
siteId: v.string(),
|
|
981
|
+
name: v.string(),
|
|
982
|
+
domains: v.array(v.string()),
|
|
983
|
+
timezone: v.union(v.string(), v.null()),
|
|
984
|
+
currency: v.string(),
|
|
985
|
+
cookieless: v.boolean(),
|
|
986
|
+
});
|
|
987
|
+
/**
|
|
988
|
+
* Exposes the reactive read surface a federated Rastro dashboard subscribes to,
|
|
989
|
+
* plus the configuration functions behind the `configure` capability, which
|
|
990
|
+
* run only when the token and the host's grant both carry the configure
|
|
991
|
+
* permission.
|
|
992
|
+
*/
|
|
993
|
+
export function exposeFederatedAnalyticsApi(component, options) {
|
|
994
|
+
const authorizeConnection = createFederatedAnalyticsConnectionAuthorizer(options);
|
|
995
|
+
const requiredPermission = (operation) => FEDERATED_CONFIGURE_OPERATIONS.has(operation)
|
|
996
|
+
? FEDERATED_ANALYTICS_CONFIGURE_PERMISSION
|
|
997
|
+
: FEDERATED_ANALYTICS_READ_PERMISSION;
|
|
998
|
+
const api = exposeAnalyticsApi(component, {
|
|
999
|
+
authorize: async (ctx, request) => {
|
|
1000
|
+
await authorizeConnection(ctx, request.siteIds, requiredPermission(request.operation));
|
|
1001
|
+
},
|
|
1002
|
+
});
|
|
1003
|
+
const rastro = new Rastro(component);
|
|
1004
|
+
return {
|
|
1005
|
+
manifest: queryGeneric({
|
|
1006
|
+
args: {},
|
|
1007
|
+
returns: federatedAnalyticsManifestValidator,
|
|
1008
|
+
handler: () => createFederatedAnalyticsManifest(),
|
|
1009
|
+
}),
|
|
1010
|
+
connectionStatus: queryGeneric({
|
|
1011
|
+
args: {},
|
|
1012
|
+
returns: federatedAnalyticsConnectionStatusValidator,
|
|
1013
|
+
handler: async (ctx) => {
|
|
1014
|
+
const connection = await authorizeConnection(ctx);
|
|
1015
|
+
return {
|
|
1016
|
+
status: "connected",
|
|
1017
|
+
protocolVersion: FEDERATED_ANALYTICS_CONNECTOR_MANIFEST.protocolVersion,
|
|
1018
|
+
capabilities: [
|
|
1019
|
+
...FEDERATED_ANALYTICS_CONNECTOR_MANIFEST.capabilities,
|
|
1020
|
+
],
|
|
1021
|
+
permissions: connection.effectivePermissions,
|
|
1022
|
+
siteCount: connection.siteIds.length,
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
}),
|
|
1026
|
+
listSites: queryGeneric({
|
|
1027
|
+
args: {},
|
|
1028
|
+
returns: v.array(federatedSiteSummaryValidator),
|
|
1029
|
+
handler: async (ctx) => {
|
|
1030
|
+
const connection = await authorizeConnection(ctx);
|
|
1031
|
+
const summaries = [];
|
|
1032
|
+
for (const siteId of connection.siteIds) {
|
|
1033
|
+
const site = await rastro.getSite(ctx, siteId);
|
|
1034
|
+
if (!site)
|
|
1035
|
+
continue;
|
|
1036
|
+
summaries.push({
|
|
1037
|
+
siteId: site._id,
|
|
1038
|
+
name: site.name,
|
|
1039
|
+
currency: site.currency ?? "USD",
|
|
1040
|
+
timezone: site.timezone ?? null,
|
|
1041
|
+
cookieless: site.cookieless,
|
|
1042
|
+
localDays: await rastro.localDays(ctx, siteId),
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
return summaries;
|
|
1046
|
+
},
|
|
1047
|
+
}),
|
|
1048
|
+
overview: api.overview,
|
|
1049
|
+
liveVisitors: api.liveVisitors,
|
|
1050
|
+
listSessions: api.listSessions,
|
|
1051
|
+
sessionJourney: api.sessionJourney,
|
|
1052
|
+
listConversions: api.listConversions,
|
|
1053
|
+
visitorJourney: api.visitorJourney,
|
|
1054
|
+
goalsReport: api.goalsReport,
|
|
1055
|
+
funnelsReport: api.funnelsReport,
|
|
1056
|
+
affiliatesReport: api.affiliatesReport,
|
|
1057
|
+
vitalsReport: api.vitalsReport,
|
|
1058
|
+
siteMap: api.siteMap,
|
|
1059
|
+
dataCoverage: api.dataCoverage,
|
|
1060
|
+
// The configure surface. `listSites` stays redacted for every reader; the
|
|
1061
|
+
// domains a site accepts are shown only to a connection allowed to change
|
|
1062
|
+
// them, and the site's currency is reported here because the ledger
|
|
1063
|
+
// forbids changing it after the first conversion is accounted.
|
|
1064
|
+
siteSettings: queryGeneric({
|
|
1065
|
+
args: { siteId: v.string() },
|
|
1066
|
+
returns: federatedSiteSettingsValidator,
|
|
1067
|
+
handler: async (ctx, args) => {
|
|
1068
|
+
await authorizeConnection(ctx, [args.siteId], FEDERATED_ANALYTICS_CONFIGURE_PERMISSION);
|
|
1069
|
+
const site = await rastro.getSite(ctx, args.siteId);
|
|
1070
|
+
if (!site) {
|
|
1071
|
+
throw new ConvexError({
|
|
1072
|
+
code: "NOT_FOUND",
|
|
1073
|
+
message: "site not found",
|
|
1074
|
+
details: null,
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
return {
|
|
1078
|
+
siteId: site._id,
|
|
1079
|
+
name: site.name,
|
|
1080
|
+
domains: site.domains,
|
|
1081
|
+
timezone: site.timezone ?? null,
|
|
1082
|
+
currency: site.currency ?? "USD",
|
|
1083
|
+
cookieless: site.cookieless,
|
|
1084
|
+
};
|
|
1085
|
+
},
|
|
1086
|
+
}),
|
|
1087
|
+
updateSite: mutationGeneric({
|
|
1088
|
+
args: {
|
|
1089
|
+
siteId: v.string(),
|
|
1090
|
+
name: v.optional(v.string()),
|
|
1091
|
+
domains: v.optional(v.array(v.string())),
|
|
1092
|
+
timezone: v.optional(v.union(v.string(), v.null())),
|
|
1093
|
+
},
|
|
1094
|
+
returns: v.null(),
|
|
1095
|
+
handler: async (ctx, args) => {
|
|
1096
|
+
await authorizeConnection(ctx, [args.siteId], FEDERATED_ANALYTICS_CONFIGURE_PERMISSION);
|
|
1097
|
+
return await rastro.updateSite(ctx, args);
|
|
1098
|
+
},
|
|
1099
|
+
}),
|
|
1100
|
+
listGoals: api.listGoals,
|
|
1101
|
+
upsertGoal: api.upsertGoal,
|
|
1102
|
+
removeGoal: api.removeGoal,
|
|
1103
|
+
listFunnels: api.listFunnels,
|
|
1104
|
+
upsertFunnel: api.upsertFunnel,
|
|
1105
|
+
removeFunnel: api.removeFunnel,
|
|
1106
|
+
listAffiliates: api.listAffiliates,
|
|
1107
|
+
upsertAffiliate: api.upsertAffiliate,
|
|
1108
|
+
removeAffiliate: api.removeAffiliate,
|
|
1109
|
+
retentionStatus: api.retentionStatus,
|
|
1110
|
+
setRetentionPolicy: api.setRetentionPolicy,
|
|
1111
|
+
disableRetentionPolicy: api.disableRetentionPolicy,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
583
1114
|
export function componentHttpPaths(prefix = "/rastro/") {
|
|
584
1115
|
const normalized = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
585
1116
|
return {
|
|
586
1117
|
events: `${normalized}/events`,
|
|
587
1118
|
tracker: `${normalized}/tracker.js`,
|
|
1119
|
+
vitals: `${normalized}/vitals.js`,
|
|
588
1120
|
health: `${normalized}/health`,
|
|
589
1121
|
};
|
|
590
1122
|
}
|