refactor: add typescript & pnpm workspace & docker file

This commit is contained in:
Ralex
2025-10-16 23:12:40 +02:00
parent 8f73241f34
commit edb7146d6d
122 changed files with 7568 additions and 8502 deletions

1
packages/common/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,13 @@
{
"name": "@rahoot/common",
"version": "1.0.0",
"type": "module",
"dependencies": {
"socket.io": "^4.8.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.3.3"
}
}

View File

@@ -0,0 +1,30 @@
export type Player = {
id: string
username: string
points: number
}
export type Answer = {
playerId: string
answerId: number
points: number
}
export type Quizz = {
subject: string
questions: {
question: string
image?: string
answers: string[]
solution: number
cooldown: number
time: number
}[]
}
export type QuizzWithId = Quizz & { id: string }
export type GameUpdateQuestion = {
current: number
total: number
}

View File

@@ -0,0 +1,72 @@
import { Server as ServerIO, Socket as SocketIO } from "socket.io"
import { Player, QuizzWithId } from "."
import { Status, StatusDataMap } from "./status"
export type Server = ServerIO<ClientToServerEvents, ServerToClientEvents>
export type Socket = SocketIO<ClientToServerEvents, ServerToClientEvents>
export type Message<K extends keyof StatusDataMap = keyof StatusDataMap> = {
gameId?: string
status: K
data: StatusDataMap[K]
}
export type MessageWithoutStatus<T = any> = {
gameId?: string
data: T
}
export type MessageGameId = {
gameId?: string
}
export interface ServerToClientEvents {
// Game events
"game:status": (data: { name: Status; data: StatusDataMap[Status] }) => void
"game:successRoom": (data: string) => void
"game:successJoin": (gameId: string) => void
"game:totalPlayers": (count: number) => void
"game:errorMessage": (message: string) => void
"game:startCooldown": () => void
"game:cooldown": (count: number) => void
"game:kick": () => void
"game:reset": () => void
"game:updateQuestion": (data: { current: number; total: number }) => void
"game:playerAnswer": (count: number) => void
"player:updateLeaderboard": (data: { leaderboard: Player[] }) => void
// Manager events
"manager:quizzList": (quizzList: QuizzWithId[]) => void
"manager:gameCreated": (data: { gameId: string; inviteCode: string }) => void
"manager:statusUpdate": (data: {
status: Status
data: StatusDataMap[Status]
}) => void
"manager:newPlayer": (player: Player) => void
"manager:removePlayer": (playerId: string) => void
"manager:errorMessage": (message: string) => void
"manager:playerKicked": (playerId: string) => void
}
export interface ClientToServerEvents {
// Manager actions
"game:create": (quizzId: string) => void
"manager:auth": (password: string) => void
"manager:kickPlayer": (
message: MessageWithoutStatus<{ playerId: string }>
) => void
"manager:startGame": (message: MessageGameId) => void
"manager:abortQuiz": (message: MessageGameId) => void
"manager:nextQuestion": (message: MessageGameId) => void
"manager:showLeaderboard": (message: MessageGameId) => void
// Player actions
"player:join": (inviteCode: string) => void
"player:login": (message: MessageWithoutStatus<{ username: string }>) => void
"player:selectedAnswer": (
message: MessageWithoutStatus<{ answerKey: number }>
) => void
// Common
disconnect: () => void
}

View File

@@ -0,0 +1,58 @@
import { Player } from ".";
export enum Status {
SHOW_ROOM = "SHOW_ROOM",
SHOW_START = "SHOW_START",
SHOW_PREPARED = "SHOW_PREPARED",
SHOW_QUESTION = "SHOW_QUESTION",
SELECT_ANSWER = "SELECT_ANSWER",
SHOW_RESULT = "SHOW_RESULT",
SHOW_RESPONSES = "SHOW_RESPONSES",
SHOW_LEADERBOARD = "SHOW_LEADERBOARD",
FINISHED = "FINISHED",
WAIT = "WAIT",
}
export type CommonStatusDataMap = {
SHOW_START: { time: number; subject: string }
SHOW_PREPARED: { totalAnswers: number; questionNumber: number }
SHOW_QUESTION: { question: string; image?: string; cooldown: number }
SELECT_ANSWER: {
question: string
answers: string[]
image?: string
time: number
totalPlayer: number
}
SHOW_RESULT: {
correct: boolean
message: string
points: number
myPoints: number
rank: number
aheadOfMe: string | null
}
WAIT: { text: string }
FINISHED: { subject: string; top: Player[] }
}
type ManagerExtraStatus = {
SHOW_ROOM: { text: string; inviteCode?: string }
SHOW_RESPONSES: {
question: string
responses: Record<number, number>
correct: number
answers: string[]
image?: string
}
SHOW_LEADERBOARD: { leaderboard: Player[] }
}
type PlayerExtraStatus = {
WAIT: { text: string }
}
export type PlayerStatusDataMap = CommonStatusDataMap & PlayerExtraStatus
export type ManagerStatusDataMap = CommonStatusDataMap & ManagerExtraStatus
export type StatusDataMap = PlayerStatusDataMap & ManagerStatusDataMap

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"noEmit": true
}
}