@iann29/rastro 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -50
- package/agent/integration.md +26 -10
- package/agent/manifest.json +10 -5
- package/agent/manifest.schema.json +14 -5
- package/dist/client/federation.d.ts +28 -8
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +12 -1
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +262 -9
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +119 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +6 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +72 -1
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +1 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/constants.d.ts +9 -2
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +31 -3
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/coverage.d.ts +1 -0
- package/dist/component/coverage.d.ts.map +1 -1
- package/dist/component/coverage.js +6 -1
- package/dist/component/coverage.js.map +1 -1
- package/dist/component/eventStore.d.ts +2 -0
- package/dist/component/eventStore.d.ts.map +1 -1
- package/dist/component/geo.d.ts +1 -0
- package/dist/component/geo.d.ts.map +1 -1
- package/dist/component/geo.js +1 -1
- package/dist/component/geo.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +65 -2
- package/dist/component/http.js.map +1 -1
- package/dist/component/identity.d.ts +12 -0
- package/dist/component/identity.d.ts.map +1 -1
- package/dist/component/identity.js +30 -2
- package/dist/component/identity.js.map +1 -1
- package/dist/component/ingest.d.ts +36 -0
- package/dist/component/ingest.d.ts.map +1 -1
- package/dist/component/ingest.js +111 -20
- package/dist/component/ingest.js.map +1 -1
- package/dist/component/live.d.ts.map +1 -1
- package/dist/component/live.js +34 -4
- package/dist/component/live.js.map +1 -1
- package/dist/component/origin.d.ts +70 -0
- package/dist/component/origin.d.ts.map +1 -0
- package/dist/component/origin.js +230 -0
- package/dist/component/origin.js.map +1 -0
- package/dist/component/platforms.d.ts +33 -0
- package/dist/component/platforms.d.ts.map +1 -0
- package/dist/component/platforms.js +328 -0
- package/dist/component/platforms.js.map +1 -0
- package/dist/component/reports.d.ts +27 -72
- package/dist/component/reports.d.ts.map +1 -1
- package/dist/component/reports.js +72 -16
- package/dist/component/reports.js.map +1 -1
- package/dist/component/rollupStore.d.ts +7 -4
- package/dist/component/rollupStore.d.ts.map +1 -1
- package/dist/component/rollupStore.js +37 -17
- package/dist/component/rollupStore.js.map +1 -1
- package/dist/component/rollups.d.ts +27 -8
- package/dist/component/rollups.d.ts.map +1 -1
- package/dist/component/rollups.js +110 -32
- package/dist/component/rollups.js.map +1 -1
- package/dist/component/sanitize.d.ts +5 -0
- package/dist/component/sanitize.d.ts.map +1 -1
- package/dist/component/sanitize.js +33 -1
- package/dist/component/sanitize.js.map +1 -1
- package/dist/component/schema.d.ts +68 -9
- package/dist/component/schema.js +21 -3
- package/dist/component/schema.js.map +1 -1
- package/dist/component/trackedLinks.d.ts +91 -0
- package/dist/component/trackedLinks.d.ts.map +1 -0
- package/dist/component/trackedLinks.js +314 -0
- package/dist/component/trackedLinks.js.map +1 -0
- package/dist/component/useragent.d.ts +6 -0
- package/dist/component/useragent.d.ts.map +1 -1
- package/dist/component/useragent.js +9 -0
- package/dist/component/useragent.js.map +1 -1
- package/dist/component/validators.d.ts +51 -11
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +35 -1
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +11 -11
- package/dist/tracker/generated.d.ts.map +1 -1
- package/dist/tracker/generated.js +11 -11
- package/dist/tracker/generated.js.map +1 -1
- package/dist/tracker/tracker.d.ts +1 -0
- package/dist/tracker/tracker.d.ts.map +1 -1
- package/dist/tracker/tracker.js +104 -47
- package/dist/tracker/tracker.js.map +1 -1
- package/dist/tracker/vitals.d.ts.map +1 -1
- package/dist/tracker/vitals.js +22 -4
- package/dist/tracker/vitals.js.map +1 -1
- package/dist/tracker.min.js +1 -1
- package/dist/vitals.min.js +1 -1
- package/docs/upgrading.md +234 -45
- package/llms.txt +9 -4
- package/package.json +2 -2
- package/src/component/_generated/api.ts +6 -0
- package/src/component/_generated/component.ts +153 -1
- package/src/component/_generated/server.ts +1 -0
- package/src/component/constants.ts +31 -3
- package/src/component/convex.config.ts +3 -0
- package/src/component/coverage.ts +7 -1
- package/src/component/geo.ts +1 -1
- package/src/component/http.ts +71 -2
- package/src/component/identity.ts +41 -2
- package/src/component/ingest.ts +146 -33
- package/src/component/live.ts +51 -4
- package/src/component/origin.ts +273 -0
- package/src/component/platforms.ts +359 -0
- package/src/component/reports.ts +98 -18
- package/src/component/rollupStore.ts +51 -20
- package/src/component/rollups.ts +141 -32
- package/src/component/sanitize.ts +39 -1
- package/src/component/schema.ts +24 -3
- package/src/component/trackedLinks.ts +384 -0
- package/src/component/useragent.ts +11 -0
- package/src/component/validators.ts +71 -0
- package/src/tracker/generated.ts +11 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Id } from "./_generated/dataModel.js";
|
|
1
|
+
import type { Doc, Id } from "./_generated/dataModel.js";
|
|
2
2
|
export declare const overview: import("convex/server").RegisteredQuery<"public", {
|
|
3
3
|
from: number;
|
|
4
4
|
interval?: "day" | "hour" | undefined;
|
|
@@ -75,6 +75,21 @@ export declare const overview: import("convex/server").RegisteredQuery<"public",
|
|
|
75
75
|
count: number;
|
|
76
76
|
revenueCents: number;
|
|
77
77
|
}[];
|
|
78
|
+
topPlatforms: {
|
|
79
|
+
value: string;
|
|
80
|
+
count: number;
|
|
81
|
+
revenueCents: number;
|
|
82
|
+
}[];
|
|
83
|
+
topChannels: {
|
|
84
|
+
value: string;
|
|
85
|
+
count: number;
|
|
86
|
+
revenueCents: number;
|
|
87
|
+
}[];
|
|
88
|
+
topEvidence: {
|
|
89
|
+
value: string;
|
|
90
|
+
count: number;
|
|
91
|
+
revenueCents: number;
|
|
92
|
+
}[];
|
|
78
93
|
metadata: {
|
|
79
94
|
visitors: {
|
|
80
95
|
basis: "bucketOccurrences" | "estimatedUnique";
|
|
@@ -94,6 +109,9 @@ export declare const overview: import("convex/server").RegisteredQuery<"public",
|
|
|
94
109
|
topGoals: "goalCompletionVolume";
|
|
95
110
|
topMediums: "eventVolume" | "sessions";
|
|
96
111
|
topOutbound: "eventVolume";
|
|
112
|
+
topPlatforms: "eventVolume" | "sessions";
|
|
113
|
+
topChannels: "eventVolume" | "sessions";
|
|
114
|
+
topEvidence: "eventVolume" | "sessions";
|
|
97
115
|
};
|
|
98
116
|
dimensionsSince: number | null;
|
|
99
117
|
engagement: "counted" | "unavailable";
|
|
@@ -227,45 +245,12 @@ export declare const siteMap: import("convex/server").RegisteredQuery<"public",
|
|
|
227
245
|
};
|
|
228
246
|
};
|
|
229
247
|
}>>;
|
|
248
|
+
type SessionRow = Omit<Doc<"sessions">, "geoLookupAttemptedAt" | "lastPageviewAt">;
|
|
230
249
|
/** Metadata for a selected session, independent of list pagination. */
|
|
231
250
|
export declare const getSession: import("convex/server").RegisteredQuery<"public", {
|
|
232
251
|
sessionId: string;
|
|
233
252
|
siteId: import("convex/values").GenericId<"sites">;
|
|
234
|
-
}, Promise<
|
|
235
|
-
_creationTime: number;
|
|
236
|
-
_id: import("convex/values").GenericId<"sessions">;
|
|
237
|
-
affiliateAttributedAt?: number | undefined;
|
|
238
|
-
affiliateExpiresAt?: number | undefined;
|
|
239
|
-
affiliateId?: import("convex/values").GenericId<"affiliates"> | undefined;
|
|
240
|
-
affiliateSlug?: string | undefined;
|
|
241
|
-
browser: string;
|
|
242
|
-
city?: string | undefined;
|
|
243
|
-
conversionCount: number;
|
|
244
|
-
country?: string | undefined;
|
|
245
|
-
currency?: string | undefined;
|
|
246
|
-
device: string;
|
|
247
|
-
durationMs: number;
|
|
248
|
-
entryPath: string;
|
|
249
|
-
eventCount: number;
|
|
250
|
-
exitPath: string;
|
|
251
|
-
lastSeenAt: number;
|
|
252
|
-
latitude?: number | undefined;
|
|
253
|
-
longitude?: number | undefined;
|
|
254
|
-
newVisitor?: boolean | undefined;
|
|
255
|
-
os: string;
|
|
256
|
-
pageviewCount: number;
|
|
257
|
-
referrer?: string | undefined;
|
|
258
|
-
revenueCents: number;
|
|
259
|
-
sessionId: string;
|
|
260
|
-
siteId: import("convex/values").GenericId<"sites">;
|
|
261
|
-
source: string;
|
|
262
|
-
startedAt: number;
|
|
263
|
-
utmCampaign?: string | undefined;
|
|
264
|
-
utmMedium?: string | undefined;
|
|
265
|
-
utmSource?: string | undefined;
|
|
266
|
-
visitorId: string;
|
|
267
|
-
visitorKey?: string | undefined;
|
|
268
|
-
} | null>>;
|
|
253
|
+
}, Promise<SessionRow | null>>;
|
|
269
254
|
/** Exact commissions alongside overview's period revenue/conversion totals. */
|
|
270
255
|
export declare const revenueSummary: import("convex/server").RegisteredQuery<"public", {
|
|
271
256
|
from: number;
|
|
@@ -287,41 +272,7 @@ export declare const listSessions: import("convex/server").RegisteredQuery<"publ
|
|
|
287
272
|
siteId: import("convex/values").GenericId<"sites">;
|
|
288
273
|
to: number;
|
|
289
274
|
}, Promise<{
|
|
290
|
-
page:
|
|
291
|
-
_creationTime: number;
|
|
292
|
-
_id: import("convex/values").GenericId<"sessions">;
|
|
293
|
-
affiliateAttributedAt?: number | undefined;
|
|
294
|
-
affiliateExpiresAt?: number | undefined;
|
|
295
|
-
affiliateId?: import("convex/values").GenericId<"affiliates"> | undefined;
|
|
296
|
-
affiliateSlug?: string | undefined;
|
|
297
|
-
browser: string;
|
|
298
|
-
city?: string | undefined;
|
|
299
|
-
conversionCount: number;
|
|
300
|
-
country?: string | undefined;
|
|
301
|
-
currency?: string | undefined;
|
|
302
|
-
device: string;
|
|
303
|
-
durationMs: number;
|
|
304
|
-
entryPath: string;
|
|
305
|
-
eventCount: number;
|
|
306
|
-
exitPath: string;
|
|
307
|
-
lastSeenAt: number;
|
|
308
|
-
latitude?: number | undefined;
|
|
309
|
-
longitude?: number | undefined;
|
|
310
|
-
newVisitor?: boolean | undefined;
|
|
311
|
-
os: string;
|
|
312
|
-
pageviewCount: number;
|
|
313
|
-
referrer?: string | undefined;
|
|
314
|
-
revenueCents: number;
|
|
315
|
-
sessionId: string;
|
|
316
|
-
siteId: import("convex/values").GenericId<"sites">;
|
|
317
|
-
source: string;
|
|
318
|
-
startedAt: number;
|
|
319
|
-
utmCampaign?: string | undefined;
|
|
320
|
-
utmMedium?: string | undefined;
|
|
321
|
-
utmSource?: string | undefined;
|
|
322
|
-
visitorId: string;
|
|
323
|
-
visitorKey?: string | undefined;
|
|
324
|
-
}[];
|
|
275
|
+
page: SessionRow[];
|
|
325
276
|
isDone: boolean;
|
|
326
277
|
continueCursor: string;
|
|
327
278
|
}>>;
|
|
@@ -353,9 +304,11 @@ export declare const sessionJourney: import("convex/server").RegisteredQuery<"pu
|
|
|
353
304
|
os: string;
|
|
354
305
|
device: string;
|
|
355
306
|
affiliateSlug?: string | undefined;
|
|
307
|
+
clid?: string[] | undefined;
|
|
356
308
|
currency?: string | undefined;
|
|
357
309
|
eventId: string;
|
|
358
310
|
href?: string | undefined;
|
|
311
|
+
inApp?: string | undefined;
|
|
359
312
|
name?: string | undefined;
|
|
360
313
|
path: string;
|
|
361
314
|
properties?: Record<string, string | number | boolean | null> | undefined;
|
|
@@ -426,9 +379,11 @@ export declare const visitorJourney: import("convex/server").RegisteredQuery<"pu
|
|
|
426
379
|
os: string;
|
|
427
380
|
device: string;
|
|
428
381
|
affiliateSlug?: string | undefined;
|
|
382
|
+
clid?: string[] | undefined;
|
|
429
383
|
currency?: string | undefined;
|
|
430
384
|
eventId: string;
|
|
431
385
|
href?: string | undefined;
|
|
386
|
+
inApp?: string | undefined;
|
|
432
387
|
name?: string | undefined;
|
|
433
388
|
path: string;
|
|
434
389
|
properties?: Record<string, string | number | boolean | null> | undefined;
|
|
@@ -629,7 +584,7 @@ export declare const dataCoverage: import("convex/server").RegisteredQuery<"publ
|
|
|
629
584
|
from: number;
|
|
630
585
|
to: number;
|
|
631
586
|
datasets: {
|
|
632
|
-
dataset: "affiliates" | "conversions" | "events" | "funnels" | "goals" | "overviewDay" | "overviewHour" | "overviewLocalDay" | "sessions" | "siteMap" | "vitals";
|
|
587
|
+
dataset: "affiliates" | "conversions" | "events" | "funnels" | "goals" | "origin" | "overviewDay" | "overviewHour" | "overviewLocalDay" | "sessions" | "siteMap" | "vitals";
|
|
633
588
|
coverage: {
|
|
634
589
|
state: "complete" | "partial" | "retained" | "unavailable";
|
|
635
590
|
generation: 1 | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reports.d.ts","sourceRoot":"","sources":["../../src/component/reports.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"reports.d.ts","sourceRoot":"","sources":["../../src/component/reports.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@ import { fail } from "./errors.js";
|
|
|
7
7
|
import { isLocalDayAligned, localDayStart, localDayStarts, localDayTimezone, sharedLocalTimezone, } from "./localTime.js";
|
|
8
8
|
import { compareJourneyEvents, hydrateEventContexts, loadSessionJourneyEvents, loadVisitorJourneyEvents, } from "./eventStore.js";
|
|
9
9
|
import { isPlainRecord } from "./guards.js";
|
|
10
|
+
import { classifyOrigin } from "./origin.js";
|
|
11
|
+
import { externalReferrer } from "./sanitize.js";
|
|
10
12
|
import { affiliateFieldsValidator, eventFieldsValidator, funnelFieldsValidator, goalFieldsValidator, sessionFieldsValidator, vitalMetricValidator, } from "./validators.js";
|
|
11
13
|
import { resolveVisitorIdentities } from "./visitors.js";
|
|
12
14
|
import { mergeVitalHistograms, VITAL_ALL, VITAL_METRICS, vitalHistogramPercentile, vitalRatingCounts, } from "./vitals.js";
|
|
@@ -145,6 +147,11 @@ export const overview = query({
|
|
|
145
147
|
topMediums: v.array(topItemValidator),
|
|
146
148
|
// Destination hosts of outbound clicks, ranked by clicks.
|
|
147
149
|
topOutbound: v.array(topItemValidator),
|
|
150
|
+
// The session's origin (origin.ts), ranked like sources: platform ids,
|
|
151
|
+
// channel ids, and the evidence that decided them.
|
|
152
|
+
topPlatforms: v.array(topItemValidator),
|
|
153
|
+
topChannels: v.array(topItemValidator),
|
|
154
|
+
topEvidence: v.array(topItemValidator),
|
|
148
155
|
metadata: v.object({
|
|
149
156
|
visitors: v.object({
|
|
150
157
|
basis: v.union(v.literal("estimatedUnique"), v.literal("bucketOccurrences")),
|
|
@@ -164,6 +171,9 @@ export const overview = query({
|
|
|
164
171
|
topGoals: v.literal("goalCompletionVolume"),
|
|
165
172
|
topMediums: breakdownBasisValidator,
|
|
166
173
|
topOutbound: v.literal("eventVolume"),
|
|
174
|
+
topPlatforms: breakdownBasisValidator,
|
|
175
|
+
topChannels: breakdownBasisValidator,
|
|
176
|
+
topEvidence: breakdownBasisValidator,
|
|
167
177
|
}),
|
|
168
178
|
// Since when every site in the request records the medium and outbound
|
|
169
179
|
// slots; null while one of them has not stamped it. A range that starts
|
|
@@ -247,10 +257,13 @@ export const overview = query({
|
|
|
247
257
|
const bucketCount = localDays
|
|
248
258
|
? bucketStarts.length
|
|
249
259
|
: Math.floor((lastBucket - firstBucket) / intervalMs) + 1;
|
|
250
|
-
const
|
|
251
|
-
|
|
260
|
+
const shards = interval === "hour" ? HOURLY_AGGREGATE_SHARDS : AGGREGATE_SHARDS;
|
|
261
|
+
const perSiteLimit = bucketCount * shards;
|
|
252
262
|
if (perSiteLimit * siteIds.length > MAX_OVERVIEW_AGGREGATE_ROWS) {
|
|
253
|
-
|
|
263
|
+
// The row budget, not MAX_REPORT_RANGE_DAYS, bounds the overview: 101
|
|
264
|
+
// days for one site and 10 for ten; 24 hours for two, 5 for ten.
|
|
265
|
+
const maximumBuckets = Math.floor(MAX_OVERVIEW_AGGREGATE_ROWS / (shards * siteIds.length));
|
|
266
|
+
fail("LIMIT_EXCEEDED", `overview range is too wide: ${siteIds.length === 1 ? "one site" : `${siteIds.length} sites`} can read at most ${maximumBuckets} ${interval}s`, { maximumAggregateRows: MAX_OVERVIEW_AGGREGATE_ROWS, maximumBuckets });
|
|
254
267
|
}
|
|
255
268
|
const totals = emptyTotals();
|
|
256
269
|
const points = new Map();
|
|
@@ -266,14 +279,26 @@ export const overview = query({
|
|
|
266
279
|
: "overviewLocalDay";
|
|
267
280
|
await assertSourceAvailable(ctx, siteIds, [overviewDataset], from);
|
|
268
281
|
for (const siteId of siteIds) {
|
|
269
|
-
|
|
282
|
+
// Iterated rather than taken whole: within the row budget a busy range
|
|
283
|
+
// can still cross the bytes a query may read. Values are length-capped,
|
|
284
|
+
// so sixteen buckets stay far below the reserve, and running out fails
|
|
285
|
+
// with REPORT_INCOMPLETE, as the feature reports do, instead of a raw
|
|
286
|
+
// limit error.
|
|
287
|
+
const buckets = [];
|
|
288
|
+
for await (const bucket of ctx.db
|
|
270
289
|
.query("aggregateBuckets")
|
|
271
290
|
.withIndex("by_siteId_and_granularity_and_bucketStart", (range) => range
|
|
272
291
|
.eq("siteId", siteId)
|
|
273
292
|
.eq("granularity", granularity)
|
|
274
293
|
.gte("bucketStart", firstBucket)
|
|
275
|
-
.lte("bucketStart", lastBucket))
|
|
276
|
-
.
|
|
294
|
+
.lte("bucketStart", lastBucket))) {
|
|
295
|
+
buckets.push(bucket);
|
|
296
|
+
if (buckets.length > perSiteLimit)
|
|
297
|
+
break;
|
|
298
|
+
if (buckets.length % 16 === 0) {
|
|
299
|
+
await requireFeatureReadHeadroom(ctx, "overview aggregate buckets");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
277
302
|
failIfIncomplete(buckets, perSiteLimit, "overview aggregate buckets");
|
|
278
303
|
for (const bucket of buckets) {
|
|
279
304
|
if (localDays &&
|
|
@@ -375,6 +400,9 @@ export const overview = query({
|
|
|
375
400
|
topGoals: top("goal", "eventVolume"),
|
|
376
401
|
topMediums: top("medium", sessionBasis),
|
|
377
402
|
topOutbound: top("outbound", "eventVolume"),
|
|
403
|
+
topPlatforms: top("platform", sessionBasis),
|
|
404
|
+
topChannels: top("channel", sessionBasis),
|
|
405
|
+
topEvidence: top("evidence", sessionBasis),
|
|
378
406
|
metadata: {
|
|
379
407
|
visitors: {
|
|
380
408
|
basis: visitorBasis,
|
|
@@ -394,6 +422,9 @@ export const overview = query({
|
|
|
394
422
|
topGoals: "goalCompletionVolume",
|
|
395
423
|
topMediums: sessionBasis,
|
|
396
424
|
topOutbound: "eventVolume",
|
|
425
|
+
topPlatforms: sessionBasis,
|
|
426
|
+
topChannels: sessionBasis,
|
|
427
|
+
topEvidence: sessionBasis,
|
|
397
428
|
},
|
|
398
429
|
dimensionsSince: controls.every((control) => control?.dimensionsSince !== undefined)
|
|
399
430
|
? Math.max(...controls.map((control) => control.dimensionsSince))
|
|
@@ -705,6 +736,22 @@ async function readRouteRollups(ctx, siteId, from, to, budget) {
|
|
|
705
736
|
}
|
|
706
737
|
return { rows, completeFrom: cutAt + DAY_MS };
|
|
707
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* A session recorded before origins existed, classified on read from what it
|
|
741
|
+
* kept (utm fields, referrer, affiliate) and marked `legacy`: it carries no
|
|
742
|
+
* click IDs or in-app signal, and it never enters the aggregates.
|
|
743
|
+
*/
|
|
744
|
+
function withOrigin(session, domains) {
|
|
745
|
+
if (session.platform !== undefined)
|
|
746
|
+
return session;
|
|
747
|
+
const origin = classifyOrigin({
|
|
748
|
+
utmSource: session.utmSource,
|
|
749
|
+
utmMedium: session.utmMedium,
|
|
750
|
+
referrer: externalReferrer(session.referrer, domains),
|
|
751
|
+
affiliate: session.affiliateSlug !== undefined,
|
|
752
|
+
});
|
|
753
|
+
return { ...session, ...origin, evidence: "legacy" };
|
|
754
|
+
}
|
|
708
755
|
/** Metadata for a selected session, independent of list pagination. */
|
|
709
756
|
export const getSession = query({
|
|
710
757
|
args: { siteId: v.id("sites"), sessionId: v.string() },
|
|
@@ -717,7 +764,8 @@ export const getSession = query({
|
|
|
717
764
|
if (!session)
|
|
718
765
|
return null;
|
|
719
766
|
const { geoLookupAttemptedAt: _geo, lastPageviewAt: _pageview, ...result } = session;
|
|
720
|
-
|
|
767
|
+
const site = await ctx.db.get("sites", args.siteId);
|
|
768
|
+
return withOrigin(result, site?.domains ?? []);
|
|
721
769
|
},
|
|
722
770
|
});
|
|
723
771
|
/** Exact commissions alongside overview's period revenue/conversion totals. */
|
|
@@ -829,7 +877,8 @@ export const listSessions = query({
|
|
|
829
877
|
.order("desc")
|
|
830
878
|
.take(take)));
|
|
831
879
|
}
|
|
832
|
-
const
|
|
880
|
+
const site = await ctx.db.get("sites", args.siteId);
|
|
881
|
+
const publicRows = rows.map(({ geoLookupAttemptedAt: _attemptedAt, lastPageviewAt: _lastPageviewAt, ...row }) => withOrigin(row, site?.domains ?? []));
|
|
833
882
|
return keysetPaginationResult(publicRows, pagination, (row) => ({
|
|
834
883
|
timestamp: row.lastSeenAt,
|
|
835
884
|
creationTime: row._creationTime,
|
|
@@ -1366,7 +1415,7 @@ export const vitalsReport = query({
|
|
|
1366
1415
|
};
|
|
1367
1416
|
},
|
|
1368
1417
|
});
|
|
1369
|
-
const coverageDatasetValidator = 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"));
|
|
1418
|
+
const coverageDatasetValidator = 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"), v.literal("origin"));
|
|
1370
1419
|
export const dataCoverage = query({
|
|
1371
1420
|
args: {
|
|
1372
1421
|
siteId: v.id("sites"),
|
|
@@ -1396,6 +1445,7 @@ export const dataCoverage = query({
|
|
|
1396
1445
|
"affiliates",
|
|
1397
1446
|
"vitals",
|
|
1398
1447
|
"siteMap",
|
|
1448
|
+
"origin",
|
|
1399
1449
|
];
|
|
1400
1450
|
return {
|
|
1401
1451
|
siteId: args.siteId,
|
|
@@ -1586,7 +1636,12 @@ async function assertSourceAvailable(ctx, siteIds, sources, from) {
|
|
|
1586
1636
|
}
|
|
1587
1637
|
async function datasetCoverageForRange(ctx, siteId, dataset, from, to) {
|
|
1588
1638
|
if (dataset === "siteMap") {
|
|
1589
|
-
return
|
|
1639
|
+
return stampedDatasetCoverage(ctx, siteId, from, to, "siteMapSince", siteMapCoverageDataset);
|
|
1640
|
+
}
|
|
1641
|
+
// Origins exist in sessions and aggregate slots from the site's stamp, and
|
|
1642
|
+
// the overview reads them from the daily aggregates retention trims.
|
|
1643
|
+
if (dataset === "origin") {
|
|
1644
|
+
return stampedDatasetCoverage(ctx, siteId, from, to, "originSince", "overviewDay");
|
|
1590
1645
|
}
|
|
1591
1646
|
// Vitals have no legacy fallback source; the rollup either covers the range
|
|
1592
1647
|
// or the coverage states say how much of it exists.
|
|
@@ -1620,17 +1675,18 @@ async function datasetCoverageForRange(ctx, siteId, dataset, from, to) {
|
|
|
1620
1675
|
return options.sort((left, right) => coverageRank(right.state) - coverageRank(left.state))[0];
|
|
1621
1676
|
}
|
|
1622
1677
|
/**
|
|
1623
|
-
*
|
|
1624
|
-
* `siteMapSince` and ends where retention cut
|
|
1625
|
-
* narrow the window when the range exceeds
|
|
1678
|
+
* A stamped dataset's coverage without reading its rows: it starts at the
|
|
1679
|
+
* site's stamp (`siteMapSince`, `originSince`) and ends where retention cut
|
|
1680
|
+
* it. The site map report may still narrow the window when the range exceeds
|
|
1681
|
+
* its row budget.
|
|
1626
1682
|
*/
|
|
1627
|
-
async function
|
|
1683
|
+
async function stampedDatasetCoverage(ctx, siteId, from, to, stamp, retainedDataset) {
|
|
1628
1684
|
const control = await ctx.db
|
|
1629
1685
|
.query("analyticsCoverage")
|
|
1630
1686
|
.withIndex("by_siteId", (range) => range.eq("siteId", siteId))
|
|
1631
1687
|
.unique();
|
|
1632
|
-
const since = control?.
|
|
1633
|
-
const retainedBefore = control?.retained.find((item) => item.dataset ===
|
|
1688
|
+
const since = control?.[stamp] ?? null;
|
|
1689
|
+
const retainedBefore = control?.retained.find((item) => item.dataset === retainedDataset)
|
|
1634
1690
|
?.before ?? null;
|
|
1635
1691
|
const unavailable = since === null ||
|
|
1636
1692
|
to < since ||
|