Skip to content

Commit c8255b5

Browse files
tknkaaclaude
andcommitted
fix: guard against empty haiyama and align all throws to GameError
- Add null check before destructuring randomHaiyama[0] in startNewGame and restartGame to prevent crash when no haiyama is available - Replace bare Error throws with GameError throughout service.ts so callers can discriminate status codes uniformly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cfaebc5 commit c8255b5

1 file changed

Lines changed: 13 additions & 9 deletions

File tree

app/features/game/service.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export async function initGame(
6060

6161
export async function startNewGame(db: DrizzleD1Database, userId: string) {
6262
const randomHaiyama = await getRandomHaiyama(db, userId);
63+
if (!randomHaiyama[0]) throw new GameError("No haiyama available", 404);
6364
const { id: haiyamaId, tiles: haiData } = randomHaiyama[0];
6465
await initGame(db, userId, haiyamaId, haiData);
6566
}
@@ -70,10 +71,11 @@ export async function tedashi(
7071
index: number,
7172
) {
7273
const state = await repo.fetchGameState(db, userId);
73-
if (!state) throw new Error("Game not found");
74-
if (state.tsumohai.length === 0) throw new Error("No tsumohai to discard");
74+
if (!state) throw new GameError("Game not found", 404);
75+
if (state.tsumohai.length === 0)
76+
throw new GameError("No tsumohai to discard");
7577
if (index < 0 || index >= state.tehai.length)
76-
throw new Error("Invalid tile index");
78+
throw new GameError("Invalid tile index");
7779

7880
const tsumohai = state.tsumohai[0];
7981
const sortedTehai = sortTehai(state.tehai);
@@ -93,8 +95,9 @@ export async function tedashi(
9395

9496
export async function tsumogiri(db: DrizzleD1Database, userId: string) {
9597
const state = await repo.fetchGameState(db, userId);
96-
if (!state) throw new Error("Game not found");
97-
if (state.tsumohai.length === 0) throw new Error("No tsumohai to discard");
98+
if (!state) throw new GameError("Game not found", 404);
99+
if (state.tsumohai.length === 0)
100+
throw new GameError("No tsumohai to discard");
98101

99102
const newHaiyama = state.haiyama.slice(1);
100103

@@ -109,7 +112,7 @@ export async function tsumogiri(db: DrizzleD1Database, userId: string) {
109112

110113
export async function jikyoku(db: DrizzleD1Database, userId: string) {
111114
const state = await repo.fetchGameState(db, userId);
112-
if (!state) throw new Error("Game not found");
115+
if (!state) throw new GameError("Game not found", 404);
113116
await repo.patchGameState(db, userId, { kyoku: state.kyoku + 1 });
114117
}
115118

@@ -118,12 +121,13 @@ async function restartGame(
118121
userId: string,
119122
): Promise<{ isGameOver: boolean }> {
120123
const state = await repo.fetchGameState(db, userId);
121-
if (!state) throw new Error("Game not found");
124+
if (!state) throw new GameError("Game not found", 404);
122125

123126
const newKyoku = state.kyoku + 1;
124127
if (newKyoku > 4) return { isGameOver: true };
125128

126129
const randomHaiyama = await getRandomHaiyama(db, userId);
130+
if (!randomHaiyama[0]) throw new GameError("No haiyama available", 404);
127131
const { id: newHaiyamaId, tiles } = randomHaiyama[0];
128132
const tehai = tiles.slice(0, 13);
129133
const tsumohai = tiles[13] ? [tiles[13]] : [];
@@ -154,8 +158,8 @@ async function recordKyoku(
154158
},
155159
) {
156160
const state = await repo.fetchGameState(db, userId);
157-
if (!state) throw new Error("Game not found");
158-
if (!state.haiyamaId) throw new Error("Haiyama ID not found");
161+
if (!state) throw new GameError("Game not found", 404);
162+
if (!state.haiyamaId) throw new GameError("Haiyama ID not found");
159163

160164
await repo.insertKyokuRecord(db, {
161165
userId,

0 commit comments

Comments
 (0)