48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
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})`,
|
|
);
|
|
}
|
|
}
|
|
}
|