# `analytics-event-schema.ts` _(no submodules)_ ## Purpose Defines the canonical Zod schema for `AnalyticsEvent` and a `validateAnalyticsEvent` parse helper. The schema lets consumers validate untrusted payloads (iframe postMessage bridges, browser extension messages, recovered IndexedDB rows) before handing them to the tracker. The helper wraps `safeParse` so the boundary failure mode is a typed `AnalyticsBrowserError(INVALID_EVENT)` carrying the full Zod issue list on `error.context.issues` — no raw `ZodError` leaks past the boundary. ## Features - Single source of truth for the on-wire and in-memory shape of an analytics event. - Required: `id`, `name`, `timestamp`, `sessionId`, `anonymousId`, `properties` (record). Optional: `userId`. - Boundary-failure translation: every Zod issue is collected into `error.context.issues` so observability tooling can render the full failure tree. - Schema is exported directly so downstream packages can `.merge` / `.extend` to add domain-specific shape (e.g. an e-commerce analytics extension). ## Usage: Basic ```ts import { validateAnalyticsEvent } from "@suaveplan/analytics-browser/schema"; const safe = validateAnalyticsEvent(unknownPayload); tracker.track(safe.name, safe.properties); ``` ## Usage: Advanced ```ts import z from "zod"; import { AnalyticsEventSchema } from "@suaveplan/analytics-browser/schema"; // Domain-specific extension — every commerce event additionally carries // a non-empty `cartId`. Composition stays within the Zod model so the // .issues path stays intact when validation fails. const CommerceEventSchema = AnalyticsEventSchema.extend({ properties: z .record(z.string(), z.unknown()) .refine((p) => typeof p.cartId === "string" && p.cartId.length > 0, { message: "missing cartId", }), }); ``` ## API Reference - `AnalyticsEventSchema: z.ZodType` — the raw Zod schema. - `validateAnalyticsEvent(input: unknown): AnalyticsEvent` — parses and returns; throws `AnalyticsBrowserError(INVALID_EVENT)` on failure with `error.context.issues` exposing the Zod issue array. ## Implementation Notes The schema deliberately uses `z.string().min(1)` rather than `z.string()` for every identifier so a producer cannot bypass validation by sending an empty string. `properties` uses `z.record(z.string(), z.unknown())` rather than a stricter shape because tracker callers attach arbitrary metadata; if a downstream consumer needs a stricter contract they SHOULD `.extend` the schema as shown above. The helper does not log on failure — that is the caller's decision — and never swallows errors silently.