Files
csgowtf/src/lib/schemas/api.schema.ts
vikingowl f583ff54a9 fix: Remove Number() conversions that corrupt uint64 IDs
JavaScript's Number type cannot accurately represent uint64 values exceeding
Number.MAX_SAFE_INTEGER (2^53-1). Converting these IDs to numbers causes
precision loss and API errors.

Root cause found:
- match/[id]/+layout.ts: `Number(params.id)` corrupted match IDs
- player/[id]/+page.ts: `Number(params.id)` corrupted player IDs

Example of the bug:
- URL param: "3638078243082338615" (correct)
- After Number(): 3638078243082339000 (rounded!)
- API response: "Match 3638078243082339000 not found"

Changes:
- Remove Number() conversions in route loaders
- Keep params.id as string throughout the application
- Update API functions to only accept string (not string | number)
- Update MatchesQueryParams.player_id type to string
- Add comprehensive transformers for legacy API responses
- Transform player stats: duo→mk_2, triple→mk_3, steamid64→id
- Build full Steam avatar URLs
- Make share_code optional (not always present)

This ensures uint64 IDs maintain full precision from URL → API → response.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 23:38:37 +01:00

80 lines
2.9 KiB
TypeScript

import { z } from 'zod';
import { matchListItemSchema } from './match.schema';
/**
* Zod schemas for API responses and error handling
*/
/** APIError schema */
export const apiErrorSchema = z.object({
error: z.string(),
message: z.string(),
status_code: z.number().int(),
timestamp: z.string().datetime().optional()
});
/** Generic APIResponse schema */
export const apiResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
z.object({
data: dataSchema,
success: z.boolean(),
error: apiErrorSchema.optional()
});
/** MatchParseResponse schema */
export const matchParseResponseSchema = z.object({
match_id: z.string().min(1), // uint64 as string to preserve precision
status: z.enum(['parsing', 'queued', 'completed', 'error']),
message: z.string(),
estimated_time: z.number().int().positive().optional()
});
/** MatchParseStatus schema */
export const matchParseStatusSchema = z.object({
match_id: z.string().min(1), // uint64 as string to preserve precision
status: z.enum(['pending', 'parsing', 'completed', 'error']),
progress: z.number().int().min(0).max(100).optional(),
error_message: z.string().optional()
});
/** MatchesListResponse schema */
export const matchesListResponseSchema = z.object({
matches: z.array(matchListItemSchema),
next_page_time: z.number().int().optional(),
has_more: z.boolean(),
total_count: z.number().int().nonnegative().optional()
});
/** MatchesQueryParams schema */
export const matchesQueryParamsSchema = z.object({
limit: z.number().int().min(1).max(100).optional(),
map: z.string().optional(),
player_id: z.number().positive().optional(),
before_time: z.number().int().positive().optional()
});
/** TrackPlayerResponse schema */
export const trackPlayerResponseSchema = z.object({
success: z.boolean(),
message: z.string()
});
/** Parser functions */
export const parseAPIError = (data: unknown) => apiErrorSchema.parse(data);
export const parseMatchParseResponse = (data: unknown) => matchParseResponseSchema.parse(data);
export const parseMatchesList = (data: unknown) => matchesListResponseSchema.parse(data);
export const parseMatchesQueryParams = (data: unknown) => matchesQueryParamsSchema.parse(data);
export const parseTrackPlayerResponse = (data: unknown) => trackPlayerResponseSchema.parse(data);
/** Safe parser functions */
export const parseMatchesListSafe = (data: unknown) => matchesListResponseSchema.safeParse(data);
export const parseAPIErrorSafe = (data: unknown) => apiErrorSchema.safeParse(data);
/** Infer TypeScript types */
export type APIErrorSchema = z.infer<typeof apiErrorSchema>;
export type MatchParseResponseSchema = z.infer<typeof matchParseResponseSchema>;
export type MatchParseStatusSchema = z.infer<typeof matchParseStatusSchema>;
export type MatchesListResponseSchema = z.infer<typeof matchesListResponseSchema>;
export type MatchesQueryParamsSchema = z.infer<typeof matchesQueryParamsSchema>;
export type TrackPlayerResponseSchema = z.infer<typeof trackPlayerResponseSchema>;