**Callsign:** Validator
Generates Zod validation schemas with custom validators, transformations, and type guards.
/plugin marketplace add markus41/claude/plugin install api-integration-helper@claude-orchestrationCallsign: Validator Model: Haiku Specialization: Zod schema generation and runtime validation
Generates Zod validation schemas for request/response validation with runtime type checking, custom validators, and transformation logic.
import { z } from 'zod';
/**
* Charge schema with full validation
*/
export const ChargeSchema = z.object({
id: z.string().regex(/^ch_[a-zA-Z0-9]{24}$/, 'Invalid charge ID format'),
object: z.literal('charge'),
amount: z.number().int().positive().max(99999999, 'Amount too large'),
currency: z.string().length(3).toLowerCase(),
status: z.enum(['succeeded', 'pending', 'failed', 'canceled']),
description: z.string().max(1000).optional(),
created: z.number().int().positive(),
metadata: z.record(z.string()).optional(),
});
export type Charge = z.infer<typeof ChargeSchema>;
import { z } from 'zod';
/**
* Email validator with custom error
*/
export const EmailSchema = z
.string()
.email('Must be a valid email address')
.refine(
(email) => !email.endsWith('@tempmail.com'),
'Temporary email addresses are not allowed'
);
/**
* Currency code validator (ISO 4217)
*/
export const CurrencySchema = z
.string()
.length(3)
.toUpperCase()
.refine(
(code) => VALID_CURRENCIES.has(code),
(code) => ({ message: `Invalid currency code: ${code}` })
);
const VALID_CURRENCIES = new Set([
'USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD', 'CHF', 'CNY',
// ... more currencies
]);
/**
* Date range validator
*/
export const DateRangeSchema = z.object({
startDate: z.string().datetime(),
endDate: z.string().datetime(),
}).refine(
(data) => new Date(data.endDate) > new Date(data.startDate),
'End date must be after start date'
);
/**
* Card number validator (Luhn algorithm)
*/
export const CardNumberSchema = z
.string()
.regex(/^\d{13,19}$/, 'Invalid card number length')
.refine(luhnCheck, 'Invalid card number');
function luhnCheck(cardNumber: string): boolean {
let sum = 0;
let isEven = false;
for (let i = cardNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cardNumber[i]);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
import { z } from 'zod';
/**
* Preprocess string to number
*/
export const AmountSchema = z.preprocess(
(val) => (typeof val === 'string' ? parseInt(val, 10) : val),
z.number().int().positive()
);
/**
* Transform to uppercase
*/
export const CountryCodeSchema = z
.string()
.length(2)
.transform((val) => val.toUpperCase());
/**
* Parse and validate date
*/
export const DateSchema = z.preprocess(
(val) => (typeof val === 'string' ? new Date(val) : val),
z.date().refine((date) => date > new Date('2000-01-01'), {
message: 'Date must be after 2000-01-01',
})
);
/**
* Sanitize and validate HTML
*/
export const SafeHTMLSchema = z
.string()
.transform((html) => sanitizeHTML(html))
.refine((html) => isValidHTML(html), 'Invalid HTML structure');
import { z } from 'zod';
/**
* Payment method discriminated union
*/
const CardPaymentSchema = z.object({
type: z.literal('card'),
card: z.object({
number: CardNumberSchema,
exp_month: z.number().int().min(1).max(12),
exp_year: z.number().int().min(new Date().getFullYear()),
cvc: z.string().regex(/^\d{3,4}$/),
}),
});
const BankAccountPaymentSchema = z.object({
type: z.literal('bank_account'),
bankAccount: z.object({
accountNumber: z.string().regex(/^\d{8,17}$/),
routingNumber: z.string().regex(/^\d{9}$/),
accountType: z.enum(['checking', 'savings']),
}),
});
const PayPalPaymentSchema = z.object({
type: z.literal('paypal'),
email: EmailSchema,
});
export const PaymentMethodSchema = z.discriminatedUnion('type', [
CardPaymentSchema,
BankAccountPaymentSchema,
PayPalPaymentSchema,
]);
export type PaymentMethod = z.infer<typeof PaymentMethodSchema>;
import { z } from 'zod';
/**
* Full charge schema
*/
export const ChargeSchema = z.object({
amount: z.number().positive(),
currency: CurrencySchema,
source: z.string(),
description: z.string().optional(),
metadata: z.record(z.string()).optional(),
});
/**
* Partial update schema
*/
export const UpdateChargeSchema = ChargeSchema.partial().refine(
(data) => Object.keys(data).length > 0,
'At least one field must be provided'
);
/**
* Required subset
*/
export const CreateChargeSchema = ChargeSchema.required({
amount: true,
currency: true,
source: true,
});
import { z } from 'zod';
/**
* Format Zod errors for API response
*/
export function formatValidationErrors(error: z.ZodError): ValidationErrorResponse {
return {
type: 'validation_error',
errors: error.errors.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
expected: 'expected' in err ? err.expected : undefined,
received: 'received' in err ? err.received : undefined,
})),
};
}
/**
* Validation wrapper with formatted errors
*/
export function validateRequest<T>(
schema: z.ZodSchema<T>,
data: unknown
): { success: true; data: T } | { success: false; error: ValidationErrorResponse } {
const result = schema.safeParse(data);
if (result.success) {
return { success: true, data: result.data };
}
return {
success: false,
error: formatValidationErrors(result.error),
};
}
/**
* Usage in API client
*/
async function createCharge(data: unknown): Promise<Charge> {
const validation = validateRequest(CreateChargeSchema, data);
if (!validation.success) {
throw new ValidationError(
'Request validation failed',
validation.error.errors
);
}
const response = await fetch('/charges', {
method: 'POST',
body: JSON.stringify(validation.data),
});
const responseData = await response.json();
return ChargeSchema.parse(responseData);
}
import { z } from 'zod';
/**
* Async validator for unique email
*/
export const UniqueEmailSchema = z.string().email().refine(
async (email) => {
const exists = await checkEmailExists(email);
return !exists;
},
'Email already exists'
);
/**
* Async validator for valid API key
*/
export const APIKeySchema = z.string().refine(
async (key) => {
const isValid = await validateAPIKey(key);
return isValid;
},
'Invalid API key'
);
async function checkEmailExists(email: string): Promise<boolean> {
// Check database
return false;
}
async function validateAPIKey(key: string): Promise<boolean> {
// Validate against API
return true;
}
import { z } from 'zod';
/**
* Type guard with runtime validation
*/
export function isCharge(value: unknown): value is Charge {
return ChargeSchema.safeParse(value).success;
}
/**
* Assertion function with validation
*/
export function assertCharge(value: unknown): asserts value is Charge {
ChargeSchema.parse(value);
}
/**
* Usage
*/
function processCharge(data: unknown) {
if (isCharge(data)) {
// data is Charge type here
console.log(data.amount);
}
assertCharge(data);
// data is Charge type here
console.log(data.amount);
}
any types in validatorsDeeply analyzes existing codebase features by tracing execution paths, mapping architecture layers, understanding patterns and abstractions, and documenting dependencies to inform new development