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>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Converts legacy CSGO:WTF API responses to the new CS2.WTF format
|
||||
*/
|
||||
|
||||
import type { MatchListItem, MatchesListResponse } from '$lib/types';
|
||||
import type { MatchListItem, MatchesListResponse, Match, MatchPlayer } from '$lib/types';
|
||||
|
||||
/**
|
||||
* Legacy API match format (from api.csgow.tf)
|
||||
@@ -21,12 +21,71 @@ export interface LegacyMatchListItem {
|
||||
game_ban: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy API match detail format
|
||||
*/
|
||||
export interface LegacyMatchDetail {
|
||||
match_id: string;
|
||||
share_code?: string;
|
||||
map: string;
|
||||
date: number; // Unix timestamp
|
||||
score: [number, number]; // [team_a, team_b]
|
||||
duration: number;
|
||||
match_result: number;
|
||||
max_rounds: number;
|
||||
parsed: boolean;
|
||||
vac: boolean;
|
||||
game_ban: boolean;
|
||||
stats?: LegacyPlayerStats[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy player stats format
|
||||
*/
|
||||
export interface LegacyPlayerStats {
|
||||
team_id: number;
|
||||
kills: number;
|
||||
deaths: number;
|
||||
assists: number;
|
||||
headshot: number;
|
||||
mvp: number;
|
||||
score: number;
|
||||
player: {
|
||||
steamid64: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
vac: boolean;
|
||||
game_ban: boolean;
|
||||
vanity_url?: string;
|
||||
};
|
||||
rank: Record<string, unknown>;
|
||||
multi_kills?: {
|
||||
duo?: number;
|
||||
triple?: number;
|
||||
quad?: number;
|
||||
ace?: number;
|
||||
};
|
||||
dmg?: Record<string, unknown>;
|
||||
flash?: {
|
||||
duration?: {
|
||||
self?: number;
|
||||
team?: number;
|
||||
enemy?: number;
|
||||
};
|
||||
total?: {
|
||||
self?: number;
|
||||
team?: number;
|
||||
enemy?: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform legacy match list item to new format
|
||||
*/
|
||||
export function transformMatchListItem(legacy: LegacyMatchListItem): MatchListItem {
|
||||
return {
|
||||
match_id: Number(legacy.match_id),
|
||||
match_id: legacy.match_id, // Keep as string to preserve uint64 precision
|
||||
map: legacy.map || 'unknown', // Handle empty map names
|
||||
date: new Date(legacy.date * 1000).toISOString(), // Convert Unix timestamp to ISO string
|
||||
score_team_a: legacy.score[0],
|
||||
@@ -49,3 +108,56 @@ export function transformMatchesListResponse(
|
||||
next_page_time: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform legacy player stats to new format
|
||||
*/
|
||||
export function transformPlayerStats(legacy: LegacyPlayerStats): MatchPlayer {
|
||||
return {
|
||||
id: legacy.player.steamid64,
|
||||
name: legacy.player.name,
|
||||
avatar: `https://avatars.steamstatic.com/${legacy.player.avatar}_full.jpg`,
|
||||
team_id: legacy.team_id,
|
||||
kills: legacy.kills,
|
||||
deaths: legacy.deaths,
|
||||
assists: legacy.assists,
|
||||
headshot: legacy.headshot,
|
||||
mvp: legacy.mvp,
|
||||
score: legacy.score,
|
||||
kast: 0, // Not provided by legacy API
|
||||
// Multi-kills: map legacy names to new format
|
||||
mk_2: legacy.multi_kills?.duo,
|
||||
mk_3: legacy.multi_kills?.triple,
|
||||
mk_4: legacy.multi_kills?.quad,
|
||||
mk_5: legacy.multi_kills?.ace,
|
||||
// Flash stats
|
||||
flash_duration_self: legacy.flash?.duration?.self,
|
||||
flash_duration_team: legacy.flash?.duration?.team,
|
||||
flash_duration_enemy: legacy.flash?.duration?.enemy,
|
||||
flash_total_self: legacy.flash?.total?.self,
|
||||
flash_total_team: legacy.flash?.total?.team,
|
||||
flash_total_enemy: legacy.flash?.total?.enemy
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform legacy match detail to new format
|
||||
*/
|
||||
export function transformMatchDetail(legacy: LegacyMatchDetail): Match {
|
||||
return {
|
||||
match_id: legacy.match_id,
|
||||
share_code: legacy.share_code || undefined,
|
||||
map: legacy.map || 'unknown',
|
||||
date: new Date(legacy.date * 1000).toISOString(),
|
||||
score_team_a: legacy.score[0],
|
||||
score_team_b: legacy.score[1],
|
||||
duration: legacy.duration,
|
||||
match_result: legacy.match_result,
|
||||
max_rounds: legacy.max_rounds,
|
||||
demo_parsed: legacy.parsed,
|
||||
vac_present: legacy.vac,
|
||||
gameban_present: legacy.game_ban,
|
||||
tick_rate: 64, // Default to 64, not provided by API
|
||||
players: legacy.stats?.map(transformPlayerStats)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user