Multiplayer mahjong in a pnpm monorepo: shared game logic, a Vue 3 client, and a Fastify + WebSocket server.
| Package | Role |
|---|---|
packages/shared |
Game engine, types, constants, test utilities (pure TypeScript) |
packages/client |
Vue 3 SPA — Pinia, Vue Router, UnoCSS, drag-and-drop |
packages/server |
Fastify HTTP API, WebSocket sync, rooms |
Both client and server depend on @mahjong-game/shared via workspace:*.
Voice and video (WebRTC) use LiveKit alongside the game WebSocket; see docs/livekit-deployment.md for environment variables, TURN/STUN, and production deployment.
- Node.js 22 (see
.nvmrc) - pnpm 10 (see root
package.jsonpackageManager) - Vite+ CLI (
vp) installed globally — used for install, dev server, lint, format, and tests in this repo (see AGENTS.md)
From the repository root:
vp installClient (Vite dev server; default port is usually 5173):
pnpm --filter @mahjong-game/client dev
# or: cd packages/client && vp devServer (listens on PORT, default 3001):
pnpm --filter @mahjong-game/server dev
# or: cd packages/server && vp run devSet PORT when you need a different port, for example:
PORT=4000 pnpm --filter @mahjong-game/server devThe browser client calls the API on a different origin than Vite in dev; the server sends CORS headers automatically. In production (NODE_ENV=production), allowed origins default to BASE_URL’s origin (same as room share links). Override with comma-separated CORS_ORIGIN (e.g. https://app.example.com,https://www.example.com) if the SPA is served from a different origin than BASE_URL.
| Command | What it does |
|---|---|
pnpm test |
Run tests in all packages |
pnpm run typecheck |
TypeScript project build + per-package typecheck where defined |
pnpm run build |
tsc --build (root) |
pnpm run lint |
Lint all packages (pnpm -r lint) |
In a single package (from that package’s directory):
vp test run # tests for current package
vp check # format + lint + typecheck (where configured)Examples with filters:
pnpm --filter @mahjong-game/shared test
pnpm --filter @mahjong-game/client exec vue-tsc --noEmitBefore committing, the project expects:
pnpm test && pnpm run typecheck && vp lintWith import.meta.env.DEV, the client exposes showcase and harness routes under /dev/* (for example /dev/harness, /dev/tiles). See packages/client/src/router/index.ts.