/** * Common Domain - Schemas * * Shared validation primitives used across all domains. */ import { z } from "zod"; export const emailSchema = z .string() .email("Please enter a valid email address") .toLowerCase() .trim(); export const passwordSchema = z .string() .min(8, "Password must be at least 8 characters") .regex(/[A-Z]/, "Password must contain at least one uppercase letter") .regex(/[a-z]/, "Password must contain at least one lowercase letter") .regex(/[0-9]/, "Password must contain at least one number") .regex(/[^A-Za-z0-9]/, "Password must contain at least one special character"); export const nameSchema = z .string() .min(1, "Name is required") .max(100, "Name must be less than 100 characters") .trim(); export const phoneSchema = z .string() .regex(/^[+]?[0-9\s\-().]{7,20}$/u, "Please enter a valid phone number") .trim(); export const countryCodeSchema = z.string().length(2, "Country code must be 2 characters"); export const currencyCodeSchema = z.string().length(3, "Currency code must be 3 characters"); export const timestampSchema = z.string().datetime("Invalid timestamp format"); export const dateSchema = z.string().date("Invalid date format"); export const moneyAmountSchema = z.number().int().nonnegative("Amount must be non-negative"); export const percentageSchema = z.number().min(0).max(100, "Percentage must be between 0 and 100"); export const genderEnum = z.enum(["male", "female", "other"]); export const statusEnum = z.enum(["active", "inactive", "pending", "suspended"]); export const priorityEnum = z.enum(["low", "medium", "high", "urgent"]); export const categoryEnum = z.enum(["technical", "billing", "account", "general"]); export const billingCycleEnum = z.enum(["Monthly", "Quarterly", "Annually", "Onetime", "Free"]); export const subscriptionBillingCycleEnum = z.enum([ "Monthly", "Quarterly", "Semi-Annually", "Annually", "Biennially", "Triennially", "One-time", "Free", ]); // ============================================================================ // Salesforce and SOQL Validation Schemas // ============================================================================ /** * Schema for validating Salesforce Account IDs */ export const salesforceAccountIdSchema = z.string().min(1, "Salesforce AccountId is required"); /** * Schema for validating Salesforce IDs (15 or 18 characters) */ export const salesforceIdSchema = z .string() .regex(/^[a-zA-Z0-9]{15,18}$/, "Invalid Salesforce ID format") .trim(); /** * Schema for validating non-empty strings */ export const nonEmptyStringSchema = z.string().min(1, "Value cannot be empty").trim(); /** * Schema for validating SOQL field names */ export const soqlFieldNameSchema = z .string() .trim() .regex(/^[A-Za-z0-9_.]+$/, "Invalid SOQL field name"); // ============================================================================ // API Response Schemas // ============================================================================ /** * Schema for successful API responses * Usage: apiSuccessResponseSchema(yourDataSchema) */ export const apiSuccessResponseSchema = (dataSchema: T) => z.object({ success: z.literal(true), data: dataSchema, }); /** * Schema for successful API acknowledgements (no payload) */ export const apiSuccessAckResponseSchema = z.object({ success: z.literal(true), }); /** * Schema for successful API responses with a human-readable message (no data payload) */ export const apiSuccessMessageResponseSchema = z.object({ success: z.literal(true), message: z.string(), }); /** * Schema for error API responses */ export const apiErrorResponseSchema = z.object({ success: z.literal(false), error: z.object({ code: z.string(), message: z.string(), details: z.unknown().optional(), }), }); /** * Discriminated union schema for API responses * Usage: apiResponseSchema(yourDataSchema) */ export const apiResponseSchema = (dataSchema: T) => z.discriminatedUnion("success", [apiSuccessResponseSchema(dataSchema), apiErrorResponseSchema]); // ============================================================================ // Pagination Schemas // ============================================================================ /** * Schema for pagination query parameters */ export const paginationParamsSchema = z.object({ page: z.coerce.number().int().positive().optional().default(1), limit: z.coerce.number().int().positive().max(100).optional().default(20), offset: z.coerce.number().int().nonnegative().optional(), }); /** * Schema for paginated response data */ export const paginatedResponseSchema = (itemSchema: T) => z.object({ items: z.array(itemSchema), total: z.number().int().nonnegative(), page: z.number().int().positive(), limit: z.number().int().positive(), hasMore: z.boolean(), }); // ============================================================================ // Query Parameter Schemas // ============================================================================ /** * Schema for common filter parameters */ export const filterParamsSchema = z.object({ search: z.string().optional(), sortBy: z.string().optional(), sortOrder: z.enum(["asc", "desc"]).optional(), }); /** * Combined query params schema (pagination + filters) */ export const queryParamsSchema = paginationParamsSchema.merge(filterParamsSchema);