Files
bscscore/src/lib/domain/validation.ts
Frank Schwenk 8a46a8a019 refactor: extract reusable library
- move reusable domain, data, state, ui code into src/lib
- update host screens to consume new library exports
- document architecture and configure path aliases
- bump astro integration dependencies for compatibility

Refs #30
2025-11-13 10:41:55 +01:00

99 lines
2.7 KiB
TypeScript

import { APP_CONFIG, VALIDATION_MESSAGES } from './constants';
import type { NewGameData } from './types';
export interface ValidationResult {
isValid: boolean;
errors: string[];
}
export function validatePlayerName(name: string): ValidationResult {
const errors: string[] = [];
const trimmedName = name.trim();
if (!trimmedName) {
errors.push(VALIDATION_MESSAGES.PLAYER_NAME_REQUIRED);
}
if (trimmedName.length > APP_CONFIG.MAX_PLAYER_NAME_LENGTH) {
errors.push(VALIDATION_MESSAGES.PLAYER_NAME_TOO_LONG);
}
return {
isValid: errors.length === 0,
errors,
};
}
export function validateGameData(data: NewGameData): ValidationResult {
const errors: string[] = [];
try {
// Validate player names
const player1Validation = validatePlayerName(data.player1);
const player2Validation = validatePlayerName(data.player2);
errors.push(...player1Validation.errors);
errors.push(...player2Validation.errors);
// Check for duplicate player names
const playerNames = [data.player1.trim(), data.player2.trim()];
if (data.player3?.trim()) {
const player3Validation = validatePlayerName(data.player3);
errors.push(...player3Validation.errors);
playerNames.push(data.player3.trim());
}
const uniqueNames = new Set(playerNames.filter(name => name.length > 0));
if (uniqueNames.size !== playerNames.filter(name => name.length > 0).length) {
errors.push(VALIDATION_MESSAGES.DUPLICATE_PLAYER_NAMES);
}
// Validate game type
if (!data.gameType?.trim()) {
errors.push(VALIDATION_MESSAGES.GAME_TYPE_REQUIRED);
}
// Validate race to
if (!data.raceTo?.trim()) {
errors.push(VALIDATION_MESSAGES.RACE_TO_REQUIRED);
} else {
const raceToNumber = parseInt(data.raceTo, 10);
if (isNaN(raceToNumber) || raceToNumber <= 0) {
errors.push(VALIDATION_MESSAGES.RACE_TO_INVALID);
}
}
} catch (error) {
console.error('Validation error:', error);
errors.push('Ein unerwarteter Validierungsfehler ist aufgetreten');
}
return {
isValid: errors.length === 0,
errors,
};
}
export function sanitizePlayerName(name: string): string {
return name
.trim()
.slice(0, APP_CONFIG.MAX_PLAYER_NAME_LENGTH)
.replace(/[^\w\s-]/g, ''); // Remove special characters except spaces and hyphens
}
export function validateRaceTo(value: string): ValidationResult {
const errors: string[] = [];
if (!value?.trim()) {
errors.push(VALIDATION_MESSAGES.RACE_TO_REQUIRED);
} else {
const numValue = parseInt(value, 10);
if (isNaN(numValue) || numValue <= 0) {
errors.push(VALIDATION_MESSAGES.RACE_TO_INVALID);
}
}
return {
isValid: errors.length === 0,
errors,
};
}