Contexto
room.ts já exporta um helper testável para clampar inteiros: clampInt(n, min, max) (src/lib/room.ts:107) — arredonda, trata NaN→min e prende em [min,max]. Mas o clamp de maxPlayers está reinventado inline em 3 pontos com Math.min(Math.max(...)):
src/app/api/rooms/route.ts:46 — Math.min(Math.max(Number(settings.maxPlayers) || 6, 2), 12)
src/app/api/rooms/[code]/route.ts (action settings) — Math.min(Math.max(Number(s.maxPlayers) || room.max_players, 2), 12)
Além da duplicação, há uma divergência sutil de contrato: o inline usa Number(x) || <default> (então 0/NaN/ausente caem no default 6 ou room.max_players), enquanto clampInt levaria NaN→min (2). Consolidar exige preservar a semântica "valor inválido → default, fora de faixa → clampa".
Escopo
- Centralizar o clamp de capacidade da sala num único ponto testado, reusando
clampInt, com o default explícito no chamador. Ex.: uma constante DEFAULT_MAX_PLAYERS = 6 + clampInt(settings.maxPlayers ?? DEFAULT_MAX_PLAYERS, MIN_PLAYERS, ABSOLUTE_MAX_PLAYERS) (o teto 12/ABSOLUTE_MAX_PLAYERS já existe em room.ts:104).
- Remover o
Math.min(Math.max(...)) inline dos dois arquivos de rota.
Acceptance criteria (verificáveis)
Dica de abordagem
clampInt já cobre arredondar + faixa; o único cuidado é o default para valor inválido, que fica no chamador via ?? DEFAULT. Refactor puro, sem tocar na área sagrada nem no banco.
Contexto
room.tsjá exporta um helper testável para clampar inteiros:clampInt(n, min, max)(src/lib/room.ts:107) — arredonda, trataNaN→mine prende em[min,max]. Mas o clamp demaxPlayersestá reinventado inline em 3 pontos comMath.min(Math.max(...)):src/app/api/rooms/route.ts:46—Math.min(Math.max(Number(settings.maxPlayers) || 6, 2), 12)src/app/api/rooms/[code]/route.ts(actionsettings) —Math.min(Math.max(Number(s.maxPlayers) || room.max_players, 2), 12)Além da duplicação, há uma divergência sutil de contrato: o inline usa
Number(x) || <default>(então0/NaN/ausente caem no default 6 ouroom.max_players), enquantoclampIntlevariaNaN→min(2). Consolidar exige preservar a semântica "valor inválido → default, fora de faixa → clampa".Escopo
clampInt, com o default explícito no chamador. Ex.: uma constanteDEFAULT_MAX_PLAYERS = 6+clampInt(settings.maxPlayers ?? DEFAULT_MAX_PLAYERS, MIN_PLAYERS, ABSOLUTE_MAX_PLAYERS)(o teto12/ABSOLUTE_MAX_PLAYERSjá existe emroom.ts:104).Math.min(Math.max(...))inline dos dois arquivos de rota.Acceptance criteria (verificáveis)
Math.min(Math.max(referente amaxPlayerssobra emsrc/app/api/rooms/**.0/NaN/"abc"→ default (6 no create;room.max_playersno settings);1→2;99→12;8→8. Coberto por teste Vitest sobre a função de clamp (direto noclampIntjá basta se o default ficar no chamador).ABSOLUTE_MAX_PLAYERS(12) e um novoMIN_PLAYERS/DEFAULT_MAX_PLAYERScomo constantes nomeadas — nada de números mágicos repetidos.pnpm typecheck+pnpm buildverdes.Dica de abordagem
clampIntjá cobre arredondar + faixa; o único cuidado é o default para valor inválido, que fica no chamador via?? DEFAULT. Refactor puro, sem tocar na área sagrada nem no banco.