Implements comprehensive type system, runtime validation, API client, and testing infrastructure for CS2.WTF. TypeScript Interfaces (src/lib/types/): - Match.ts: Match, MatchPlayer, MatchListItem types - Player.ts: Player, PlayerMeta, PlayerProfile types - RoundStats.ts: Round economy and performance data - Weapon.ts: Weapon statistics with hit groups (HitGroup, WeaponType enums) - Message.ts: Chat messages with filtering support - api.ts: API responses, errors, and APIException class - Complete type safety with strict null checks Zod Schemas (src/lib/schemas/): - Runtime validation for all data models - Schema parsers with safe/unsafe variants - Special handling for backend typo (looses → losses) - Share code validation regex - CS2-specific validations (rank 0-30000, MR12 rounds) API Client (src/lib/api/): - client.ts: Axios-based HTTP client with error handling - Request cancellation support (AbortController) - Automatic retry logic for transient failures - Timeout handling (10s default) - Typed APIException errors - players.ts: Player endpoints (profile, meta, track/untrack, search) - matches.ts: Match endpoints (parse, details, weapons, rounds, chat, search) - Zod validation on all API responses MSW Mock Handlers (src/mocks/): - fixtures.ts: Comprehensive mock data for testing - handlers/players.ts: Mock player API endpoints - handlers/matches.ts: Mock match API endpoints - browser.ts: Browser MSW worker for development - server.ts: Node MSW server for Vitest tests - Realistic responses with delays and pagination - Safe integer IDs to avoid precision loss Configuration: - .env.example: Complete environment variable documentation - src/vite-env.d.ts: Vite environment type definitions - All strict TypeScript checks passing (0 errors, 0 warnings) Features: - Cancellable requests for search (prevent race conditions) - Data normalization (backend typo handling) - Comprehensive error types (NetworkError, Timeout, etc.) - Share code parsing and validation - Pagination support for players and matches - Mock data for offline development and testing Build Status: ✓ Production build successful Type Check: ✓ 0 errors, 0 warnings Lint: ✓ All checks passed Phase 3 Completion: 100% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
63 lines
2.4 KiB
TypeScript
63 lines
2.4 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* Zod schemas for Round Statistics data models
|
|
*/
|
|
|
|
/** RoundStats schema */
|
|
export const roundStatsSchema = z.object({
|
|
round: z.number().int().positive(),
|
|
bank: z.number().int().nonnegative(),
|
|
equipment: z.number().int().nonnegative(),
|
|
spent: z.number().int().nonnegative(),
|
|
kills_in_round: z.number().int().nonnegative().optional(),
|
|
damage_in_round: z.number().int().nonnegative().optional(),
|
|
match_player_id: z.number().positive().optional(),
|
|
player_id: z.number().positive().optional()
|
|
});
|
|
|
|
/** RoundDetail schema (with player breakdown) */
|
|
export const roundDetailSchema = z.object({
|
|
round: z.number().int().positive(),
|
|
winner: z.number().int().min(2).max(3), // 2 = T, 3 = CT
|
|
win_reason: z.string(),
|
|
players: z.array(roundStatsSchema)
|
|
});
|
|
|
|
/** MatchRoundsResponse schema */
|
|
export const matchRoundsResponseSchema = z.object({
|
|
match_id: z.number().positive(),
|
|
rounds: z.array(roundDetailSchema)
|
|
});
|
|
|
|
/** TeamRoundStats schema */
|
|
export const teamRoundStatsSchema = z.object({
|
|
round: z.number().int().positive(),
|
|
team_id: z.number().int().min(2).max(3),
|
|
total_bank: z.number().int().nonnegative(),
|
|
total_equipment: z.number().int().nonnegative(),
|
|
avg_equipment: z.number().nonnegative(),
|
|
total_spent: z.number().int().nonnegative(),
|
|
winner: z.number().int().min(2).max(3).optional(),
|
|
win_reason: z
|
|
.enum(['elimination', 'bomb_defused', 'bomb_exploded', 'time', 'target_saved'])
|
|
.optional(),
|
|
buy_type: z.enum(['eco', 'semi-eco', 'force', 'full']).optional()
|
|
});
|
|
|
|
/** Parser functions */
|
|
export const parseRoundStats = (data: unknown) => roundStatsSchema.parse(data);
|
|
export const parseRoundDetail = (data: unknown) => roundDetailSchema.parse(data);
|
|
export const parseMatchRounds = (data: unknown) => matchRoundsResponseSchema.parse(data);
|
|
export const parseTeamRoundStats = (data: unknown) => teamRoundStatsSchema.parse(data);
|
|
|
|
/** Safe parser functions */
|
|
export const parseRoundStatsSafe = (data: unknown) => roundStatsSchema.safeParse(data);
|
|
export const parseMatchRoundsSafe = (data: unknown) => matchRoundsResponseSchema.safeParse(data);
|
|
|
|
/** Infer TypeScript types */
|
|
export type RoundStatsSchema = z.infer<typeof roundStatsSchema>;
|
|
export type RoundDetailSchema = z.infer<typeof roundDetailSchema>;
|
|
export type MatchRoundsResponseSchema = z.infer<typeof matchRoundsResponseSchema>;
|
|
export type TeamRoundStatsSchema = z.infer<typeof teamRoundStatsSchema>;
|