Fas 1a: analytics tracker, ingestion endpoint, admin dashboards
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
import { PRODUCT_ANALYTICS_EVENT_NAMES } from "@app/shared-types";
|
||||
|
||||
export const MAX_ANALYTICS_BATCH_SIZE = 50;
|
||||
export const MAX_ANALYTICS_PROPERTY_SIZE = 8 * 1024; // 8 KiB per event properties JSON
|
||||
|
||||
const eventNameSchema = z.enum(PRODUCT_ANALYTICS_EVENT_NAMES);
|
||||
|
||||
const analyticsEventSchema = z.object({
|
||||
name: eventNameSchema,
|
||||
occurredAt: z.string().refine((v) => !Number.isNaN(Date.parse(v))).optional(),
|
||||
anonymousId: z.string().max(64).optional(),
|
||||
sessionId: z.string().max(64).optional(),
|
||||
householdId: z.string().uuid().optional(),
|
||||
appVersion: z.string().max(32).optional(),
|
||||
platform: z.enum(["ios", "android", "web"]).optional(),
|
||||
locale: z.string().max(16).optional(),
|
||||
experimentVariant: z.string().max(128).optional(),
|
||||
properties: z.record(z.string(), z.unknown()).default({}),
|
||||
});
|
||||
|
||||
export const analyticsBatchInputSchema = z.object({
|
||||
events: z
|
||||
.array(analyticsEventSchema)
|
||||
.min(1, "At least one event is required")
|
||||
.max(MAX_ANALYTICS_BATCH_SIZE, `Max ${MAX_ANALYTICS_BATCH_SIZE} events per batch`),
|
||||
});
|
||||
|
||||
export type AnalyticsBatchInput = z.infer<typeof analyticsBatchInputSchema>;
|
||||
|
||||
/**
|
||||
* Validate that the JSON-stringified properties of each event fit within the
|
||||
* per-event byte budget. Call this after zod parsing.
|
||||
*/
|
||||
export function validateAnalyticsPropertySize(events: AnalyticsBatchInput["events"]): void {
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const size = new Blob([JSON.stringify(events[i]?.properties ?? {})]).size;
|
||||
if (size > MAX_ANALYTICS_PROPERTY_SIZE) {
|
||||
throw new Error(
|
||||
`Event ${i} properties exceed ${MAX_ANALYTICS_PROPERTY_SIZE} bytes (${size})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,3 +12,4 @@ export * from "./recommendations.js";
|
||||
export * from "./memory.js";
|
||||
export * from "./subscriptions.js";
|
||||
export * from "./locale.js";
|
||||
export * from "./analytics.js";
|
||||
|
||||
Reference in New Issue
Block a user