63 lines
3.2 KiB
Markdown
63 lines
3.2 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Purpose
|
|
|
|
**Segnapunti Anto** is a real-time volleyball scoreboard PWA. An Express/WebSocket server hosts the game state; two separate web interfaces — a **display** (public scoreboard) and a **controller** (operator panel) — stay in sync via WebSocket.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run dev # Vite dev server — display: :5173/display, controller: :5173/controller
|
|
npm run serve # Build + run production — display: :3000/display, controller: :3000/controller
|
|
|
|
npm run test # Vitest watch mode
|
|
npm run test:all # All Vitest suites once (unit, integration, component, stress)
|
|
npm run test:unit # Unit + integration only
|
|
npm run test:component # Vue component tests
|
|
npm run test:stress # Load tests (50+ concurrent clients)
|
|
npm run test:e2e # Playwright E2E (requires servers to be running via npm run serve)
|
|
npm run test:e2e:ui # Playwright with interactive UI
|
|
```
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Controller (Vue) ──WebSocket──┐
|
|
Display (Vue) ──WebSocket──┤── websocket-handler.js ── gameState.js
|
|
│ │
|
|
│ └── persist.js ── .segnapunti/state.json
|
|
```
|
|
|
|
All game logic lives in `src/gameState.js` as three pure functions:
|
|
- `createInitialState()` — returns the initial state
|
|
- `applyAction(state, action)` — immutable reducer (deep-clones via `structuredClone`)
|
|
- `checkVittoria(state)` — volleyball win conditions (25-point sets with 2-point margin, 15-point final set)
|
|
|
|
`src/websocket-handler.js` receives actions, validates that the sender is a registered controller (not just a display), calls `applyAction`, broadcasts the new state to all clients, then calls `onStateChange` to persist state to disk.
|
|
|
|
`server.js` (Express) serves both interfaces on port 3000: `/display` → `dist/index.html`, `/controller` → `dist/controller.html`. Single WebSocket endpoint at `/ws`.
|
|
|
|
In development, `vite-plugin-websocket.js` embeds the WebSocket server inside the Vite dev server with URL rewrite middleware for `/display` and `/controller`.
|
|
|
|
## Key Design Constraints
|
|
|
|
- **All game rules on the server** — clients are pure UI; the server is the source of truth.
|
|
- **Role-based WebSocket** — clients register as `display` or `controller`; only controllers may send actions.
|
|
- **Immutable state** — `applyAction` never mutates; always returns a new state object.
|
|
- **Single-controller intent** — the design targets one active controller and one display; no conflict resolution exists for simultaneous controllers.
|
|
- **Persistent state** — `src/persist.js` saves state to `.segnapunti/state.json` after every action and loads it on server startup.
|
|
|
|
## Test Layout
|
|
|
|
| Suite | Path | Runner |
|
|
|-------|------|--------|
|
|
| Unit | `tests/unit/` | Vitest + Node |
|
|
| Integration | `tests/integration/` | Vitest + Node |
|
|
| Component | `tests/component/` | Vitest + Happy-DOM |
|
|
| Stress | `tests/stress/` | Vitest + Node |
|
|
| E2E | `tests/e2e/` | Playwright (Chromium, Firefox, Mobile Chrome) |
|
|
|
|
E2E tests run serially (`workers: 1`) to avoid WebSocket state races. Run `npm run serve` before `npm run test:e2e`.
|