91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
/**
|
|
* @file SearchParamValidators.ts
|
|
* Pure validation logic for search parameters
|
|
* No side effects, no dependencies
|
|
*/
|
|
|
|
export interface ValidationResult {
|
|
isValid: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
export class SearchParamValidators {
|
|
// Auth validators
|
|
static validateReturnTo(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
if (!value.startsWith('/')) {
|
|
return { isValid: false, errors: ['returnTo must start with /'] };
|
|
}
|
|
if (value.includes('://') || value.includes('//')) {
|
|
return { isValid: false, errors: ['returnTo must be a relative path'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
static validateToken(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
if (value.length === 0) {
|
|
return { isValid: false, errors: ['token cannot be empty'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
static validateEmail(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
if (!value.includes('@')) {
|
|
return { isValid: false, errors: ['email must be valid'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
// Sponsor validators
|
|
static validateCampaignType(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
const validTypes = ['leagues', 'teams', 'drivers', 'races', 'platform'];
|
|
if (!validTypes.includes(value)) {
|
|
return { isValid: false, errors: [`type must be one of: ${validTypes.join(', ')}`] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
// Pagination validators
|
|
static validatePage(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
const num = parseInt(value);
|
|
if (isNaN(num) || num < 1) {
|
|
return { isValid: false, errors: ['page must be a positive integer'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
static validateLimit(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
const num = parseInt(value);
|
|
if (isNaN(num) || num < 1) {
|
|
return { isValid: false, errors: ['limit must be a positive integer'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
// Sorting validators
|
|
static validateOrder(value: string | null): ValidationResult {
|
|
if (value === null) return { isValid: true, errors: [] };
|
|
if (!['asc', 'desc'].includes(value)) {
|
|
return { isValid: false, errors: ['order must be asc or desc'] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
// Generic validators
|
|
static validateRequired(value: string | null, fieldName: string): ValidationResult {
|
|
if (value === null || value.length === 0) {
|
|
return { isValid: false, errors: [`${fieldName} is required`] };
|
|
}
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
|
|
static validateOptional(_value: string | null): ValidationResult {
|
|
return { isValid: true, errors: [] };
|
|
}
|
|
}
|