Development
Package manager: npm (.npmrc with legacy-peer-deps=true for the
peer conflict between openapi-typescript and TypeScript 6 — keep it).
Commands
Section titled “Commands”npm install # install (reproducible via package-lock.json)npm run dev # dev server on http://localhost:3000npm run typecheck # tsc --noEmitnpm run check # biome check + typecheck (run before finishing a task)npm run format # biome formatnpm run lint # biome lintnpm run build # production client + SSR buildnpm run preview # serves the production buildnpm test # vitest runCode generation
Section titled “Code generation”npm run api:gen # downloads /docs-json → scripts/openapi.latest.json (gitignored) # → openapi-typescript → src/core/api/generated/schema.ts (committed)npm run generate-routes # tsr generate → src/routeTree.gen.ts (after every route change)Environment
Section titled “Environment”Copy .env.example to .env if needed:
VITE_API_URL=/api/v1The default /api/v1 value is relative (same origin): in dev, the
vite.config.ts proxy relays /api/* to the backend
https://dev.sim.strife-cyber.org (that backend’s CORS rejects
localhost:*). An absolute URL (https://host/api/v1) remains possible if
the frontend is hosted on another domain.
VITE_API_URL is not secret (exposed to the browser). JWT tokens are
never put in a .env or in the bundle as constants.
Production (Docker)
Section titled “Production (Docker)”docker compose up --build # build + run → http://localhost:3004The compose host port is 3004 (3000 is captured by WSL on this machine);
the container’s internal port stays 3000 (PORT).
The image (multi-stage Dockerfile) builds dist/ then runs it via
prod-server.mjs: a node:http host that performs SSR rendering
(dist/server/server.js), serves assets (dist/client/), and relays
/api/* to the backend defined by the API_TARGET env var (same pattern
as the dev Vite proxy — the browser only calls the same origin, backend
CORS does not apply). Without API_TARGET, /api/* answers 404.
VITE_API_URLis inlined at build (default/api/v1, same origin).PORT(default3000) andAPI_TARGETare container variables.docker composepointsAPI_TARGETat the dev instance by default — replace it with the production backend URL on deployment.npm run build && node prod-server.mjsreproduces the runtime locally (no Docker).
Typical workflow
Section titled “Typical workflow”npm run devfor development (HMR: routes, tailwind).- Before finishing:
npm run checkthennpm test.
Don’ts
Section titled “Don’ts”- ❌ Use
pnpmoryarn(the npm lockfile must remain the source of truth). - ❌ Commit
scripts/openapi.latest.json(gitignored). - ❌ Remove
legacy-peer-depsfrom.npmrc.
