169 lines
5.1 KiB
TypeScript
Raw Normal View History

/**
* Common Domain - Schemas
*
* Shared validation primitives used across all domains.
*/
import { z } from "zod";
import { createPaginationSchema } from "../toolkit/validation/helpers.js";
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 billingCycleSchema = z.enum([
"Monthly",
"Quarterly",
"Semi-Annually",
"Annually",
"Biennially",
"Triennially",
"One-time",
"Free",
]);
export type BillingCycle = z.infer<typeof billingCycleSchema>;
// ============================================================================
// 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 action acknowledgement responses (no payload).
* Used for endpoints that confirm an operation succeeded without returning data.
*
* @example
* // Controller returns:
* return {}; // Empty object as acknowledgement
*/
export const actionAckResponseSchema = z.object({});
/**
* Schema for action responses with a human-readable message.
* Used for endpoints that return a confirmation message.
*
* @example
* // Controller returns:
* return { message: "SIM top-up completed successfully" };
*/
export const actionMessageResponseSchema = z.object({
message: z.string(),
});
/**
* Schema for error API responses.
* Used by the exception filter to return structured errors.
*/
export const apiErrorResponseSchema = z.object({
success: z.literal(false),
error: z.object({
code: z.string(),
message: z.string(),
// Intentionally z.unknown() — error details vary by error type
details: z.unknown().optional(),
}),
});
// ============================================================================
// Pagination Schemas
// ============================================================================
/**
* Schema for pagination query parameters
*/
export const paginationParamsSchema = createPaginationSchema({ defaultLimit: 20 });
/**
* Schema for paginated response data
*/
export const paginatedResponseSchema = <T extends z.ZodTypeAny>(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);