import * as z from "zod/mini"; import { appEventIdentityTypeSchema } from "./app-event.js"; import { clientMetaSchema } from "./client-meta.js"; import { ErrorEventSchema as errorEventSchema } from "./errors.js"; const TELEMETRY_SNAKE_CASE_KEY_REGEX = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/; export const telemetryIdentityTypeSchema = appEventIdentityTypeSchema; export type TelemetryIdentityType = z.output< typeof telemetryIdentityTypeSchema >; export const telemetryIdentitySchema = z.object({ type: telemetryIdentityTypeSchema, value: z.string(), }); export type TelemetryIdentity = z.output; const nullishStringSchema = z.optional(z.union([z.string(), z.null()])); export const telemetryContextSchema = z.object({ page: z.object({ path: nullishStringSchema, referrer: nullishStringSchema, search: nullishStringSchema, /** When debug is off, only origin is sent (origin-level). When debug is on, path/referrer/search may be present. */ origin: nullishStringSchema, }), widgetVersion: z.string(), clientMeta: z.optional(clientMetaSchema), }); export type TelemetryContext = z.output; export const telemetryEventSchema = z.object({ type: z.string(), timestamp: z.number(), apiKey: z.string(), /** When true, identities and full page context are included. When false, no identities and page is origin-only. */ debug: z.boolean(), identities: z.array(telemetryIdentitySchema), context: telemetryContextSchema, properties: z.optional(z.record(z.string(), z.unknown())), }); export type WidgetTelemetryEvent = z.output; export const serverTelemetrySourceSchema = z.object({ runtime: z.enum(["web", "core", "worker", "tooling"]), subsystem: z.enum([ "access_control", "agent_runner", "atlas", "delivery", "dispatch_cron", "analytics_delivery_cron", "identity_cron", "flow_assignment", "revocation", "identity_generation", "segment_ingest", "background_migration", "server_runtime", "ci_runner", "crm_activity", ]), environment: z.optional(z.union([z.string(), z.null()])), }); export type ServerTelemetrySource = z.output< typeof serverTelemetrySourceSchema >; export const serverTelemetryProjectSchema = z.object({ apiKey: z.optional(z.union([z.string(), z.null()])), organizationId: z.optional(z.union([z.string(), z.null()])), integrationId: z.optional(z.union([z.string(), z.null()])), }); export type ServerTelemetryProject = z.output< typeof serverTelemetryProjectSchema >; export const serverTelemetryTraceSchema = z.object({ effectRequestId: z.optional(z.union([z.string(), z.null()])), requestId: z.optional(z.union([z.string(), z.null()])), jobKey: z.optional(z.union([z.string(), z.null()])), lockId: z.optional(z.union([z.string(), z.null()])), processInstanceId: z.optional(z.union([z.string(), z.null()])), signalId: z.optional(z.union([z.string(), z.null()])), }); export type ServerTelemetryTrace = z.output; export const serverTelemetryDimensionValueSchema = z.union([ z.string(), z.number(), z.boolean(), z.null(), ]); export type ServerTelemetryDimensionValue = z.output< typeof serverTelemetryDimensionValueSchema >; export const telemetrySnakeCaseKeySchema = z.string().check( z.regex(TELEMETRY_SNAKE_CASE_KEY_REGEX, { message: "Telemetry dimensions and metrics must use snake_case keys.", }), ); export type TelemetrySnakeCaseKey = z.output< typeof telemetrySnakeCaseKeySchema >; export const serverTelemetryDimensionsSchema = z.optional( z.record(telemetrySnakeCaseKeySchema, serverTelemetryDimensionValueSchema), ); export type ServerTelemetryDimensions = z.output< typeof serverTelemetryDimensionsSchema >; export const serverTelemetryDiagnosticsSchema = z.optional( z.record(z.string(), z.unknown()), ); export type ServerTelemetryDiagnostics = z.output< typeof serverTelemetryDiagnosticsSchema >; export const serverTelemetryMetricsSchema = z.optional( z.record(telemetrySnakeCaseKeySchema, z.number()), ); export type ServerTelemetryMetrics = z.output< typeof serverTelemetryMetricsSchema >; export const serverTelemetryTruncationStageSchema = z.enum([ "diagnostics_clipped", "diagnostics_removed", "minimal", ]); export type ServerTelemetryTruncationStage = z.output< typeof serverTelemetryTruncationStageSchema >; export const serverTelemetryTruncationSchema = z.object({ applied: z.literal(true), clippedDiagnosticsKeys: z.optional(z.array(z.string())), droppedDiagnosticsKeys: z.optional(z.array(z.string())), finalEncodedLength: z.number(), originalEncodedLength: z.number(), stage: serverTelemetryTruncationStageSchema, }); export type ServerTelemetryTruncation = z.output< typeof serverTelemetryTruncationSchema >; export const serverTelemetryEventSchema = z.object({ type: z.string(), timestamp: z.number(), source: serverTelemetrySourceSchema, project: z.optional(serverTelemetryProjectSchema), trace: z.optional(serverTelemetryTraceSchema), dimensions: serverTelemetryDimensionsSchema, metrics: serverTelemetryMetricsSchema, diagnostics: serverTelemetryDiagnosticsSchema, truncation: z.optional(serverTelemetryTruncationSchema), }); export type ServerTelemetryEvent = z.output; export type TelemetryEvent = WidgetTelemetryEvent; export const telemetryEnvelopeSchema = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("widget_event"), event: telemetryEventSchema, }), z.object({ kind: z.literal("server_event"), event: serverTelemetryEventSchema, }), z.object({ kind: z.literal("error"), event: errorEventSchema, }), ]); export type TelemetryEnvelope = z.output; export type TelemetryEnvelopeKind = TelemetryEnvelope["kind"]; export type TelemetryEnvelopeEvent = Extract< TelemetryEnvelope, { kind: K } >["event"];