import * as z from "zod/mini"; export const IDENTITY_TYPE_VALUES = [ "anonymousId", "userId", "traits.email", "traits.phone", "context.device.id", "context.device.advertisingId", "context.device.token", ] as const; export type StandardIdentityTypeValue = (typeof IDENTITY_TYPE_VALUES)[number]; export type IdentityTypeValue = StandardIdentityTypeValue; const IDENTITY_TYPE_ALIAS_SUGGESTIONS: Record< string, StandardIdentityTypeValue > = { advertising_id: "context.device.advertisingId", anonymous_id: "anonymousId", device_id: "context.device.id", device_token: "context.device.token", email: "traits.email", phone: "traits.phone", phone_number: "traits.phone", user_id: "userId", }; const COMMON_IDENTITY_TYPE_MAPPINGS = [ ["email", "traits.email"], ["phone", "traits.phone"], ["user_id", "userId"], ] as const; const normalizeIdentityTypeAliasKey = (value: string): string => value.trim().toLowerCase().replace(/-/g, "_"); export const isIdentityTypeValue = ( value: string, ): value is IdentityTypeValue => IDENTITY_TYPE_VALUES.some((candidate) => candidate === value); const parseIdentityTypeValue = (value: string): IdentityTypeValue | null => isIdentityTypeValue(value) ? value : null; export const getIdentityTypeSuggestion = ( value: string, ): IdentityTypeValue | null => IDENTITY_TYPE_ALIAS_SUGGESTIONS[normalizeIdentityTypeAliasKey(value)] ?? null; export const buildIdentityTypeGuidanceMessage = (value: string): string => { const normalizedValue = value.trim(); const displayValue = normalizedValue.length > 0 ? `"${normalizedValue}"` : "that value"; const suggestion = getIdentityTypeSuggestion(value); if (suggestion) { return `Unsupported identity type ${displayValue}. Use "${suggestion}" instead.`; } const mappingExamples = COMMON_IDENTITY_TYPE_MAPPINGS.map( ([from, to]) => `${from} -> ${to}`, ).join(", "); return `Unsupported identity type ${displayValue}. Use one of: ${IDENTITY_TYPE_VALUES.join(", ")}. For external IDs, send Segment-compatible context.externalIds entries instead. Common mappings: ${mappingExamples}.`; }; const identityTypeInputSchema = z.string().check( z.trim(), z.minLength(1, { error: "Identity type is required." }), z.check((ctx) => { if (isIdentityTypeValue(ctx.value)) { return; } ctx.issues.push({ code: "custom", input: ctx.value, message: buildIdentityTypeGuidanceMessage(ctx.value), continue: false, }); }), ); export const appEventIdentityTypeSchema = z.pipe( identityTypeInputSchema, z.transform((value): IdentityTypeValue => { const parsed = parseIdentityTypeValue(value); if (parsed === null) { throw new Error( `Identity type should have been validated before transform: ${value}`, ); } return parsed; }), );